# Conversation - 2026-09-09_2115
session_id: 28b0f62c-39a1-4ca6-bce9-20ac49c53b31

## User

review all the updates made today and evaluate how bot performade after the changes

## Assistant

Here's today's (2026-09-09) review — a continuation of yesterday's 9-phase prediction-engine rebuild going live.

## What changed today

1. **Crash fix** — bot crashed 19 minutes into the session (09:31:05) with a `TypeError: NoneType - NoneType` in `winner_score.py`. Root cause: `prediction_pipeline.py` was passing an unresolved `as_of=None` through to score-history storage. Fixed by resolving the timestamp at the top of `evaluate_entry_pipeline()`, matching the pattern already used elsewhere in the codebase. Bot restarted 11:12:11.

2. **BGC overextension gap found and closed** — the bot entered BGC at $12.325 while RSI(14) was 86.7 and price sat exactly at the session high (confirmed via Yahoo Finance). Root cause: `entry_score.py` (the active decision engine) has no RSI/overextension check at all — only a diluted `reward_risk_ratio` component, and yesterday's threshold loosening (`min_risk_score` 60→45) let it slip through despite a catastrophic 0.03 reward/risk ratio (min 1.5). Fix shipped: a new `extension_from_open_pct` feature plus a hard tier in `entry_score.py` — any symbol >0.3% above its session open now needs all five component scores at 100 (instead of their normal floors) to enter. Bot restarted 12:27:35 to load it.

## How the bot performed

**Full day: 1 trade, 0 wins, 1 loss, -$19.86.**

- **09:31–11:12**: down (crash), no trading possible.
- **11:35**: entered BGC — the one fill of the day, and it was the overextension case above. Stopped out at 12:17 for -$19.86.
- **12:29 onward (after the new gate went live)**: the fix held cleanly — 93 rejections across 10 symbols (JOBY×20, RDW×16, SRAD×14, GDYN×10, RWAY×8, CGNT×7, TJGC×5, INFQ×5, NXE×4, BTG×4) were blocked specifically for being extended from open, zero further entries, zero errors or tracebacks the rest of the session.

**Assessment:** the crash fix worked (clean restart, no recurrence). The overextension fix also worked as designed — it's demonstrably suppressing exactly the pattern that burned BGC, with no regressions. But this is one day's evidence: the gate has now been proven to *block* aggressively, not yet proven that it isn't blocking real winners too (no multi-day backtest re-run has happened since Phase 5/6 shipped). That backtest re-run remains the open item before trusting today's threshold combination over a longer stretch.

I've updated the two relevant memory records (`project_bgc_overextension_gap`, `project_prediction_upgrade_2026_09_08`) with today's EOD outcome so this is available in future sessions.

## User

can you cross reference from bot decisions and the yahoo finance chart for all 10 symbols rejected like joby was rejected 20 times can you check logs chart and what the bot was seeing at each time and repeat it for all the other symbols and put everything in a table that we can review, including your evaluation

## Assistant

This needs cross-referencing ~93 individual log events against real Yahoo Finance 1-minute price data for 10 symbols, then building a reviewable table — that's a lot of raw data (log greps + price bars) I don't need sitting in my own context. I'll fork this out and have the fork publish the result as an artifact.

## Assistant

I've kicked off a background task pulling every rejection event for JOBY, RDW, SRAD, GDYN, RWAY, CGNT, TJGC, INFQ, NXE, and BTG from today's log, fetching real 1-minute Yahoo Finance bars for each, and cross-referencing what price was actually doing at each block point. It'll publish the result as a reviewable page with per-symbol verdicts (correct filter vs. missed opportunity) plus the detailed event-by-event breakdown. I'll let you know as soon as it's back.

## User

<task-notification>
<task-id>a0bdabbd05a5842fe</task-id>
<tool-use-id>toolu_01AK8QCemdimVZwLsJMmydqZ</tool-use-id>
<output-file>/tmp/claude-0/-var-www-screener-premarket/28b0f62c-39a1-4ca6-bce9-20ac49c53b31/tasks/a0bdabbd05a5842fe.output</output-file>
<status>completed</status>
<summary>Agent "Cross-reference rejected symbols vs Yahoo charts" finished</summary>
<note>A task-notification fires each time this agent stops with no live background children of its own. The user can send it another message and resume it, so the same task-id may notify more than once.</note>
<result>Published: **[Extension Gate Review](https://claude.ai/code/artifact/0a262866-e409-4eb0-8df0-239beb9834b4)**

Cross-referenced all 93 rejection events across the 10 blocked symbols against real Yahoo Finance 1-minute bars for 2026-09-09.

**Verdict breakdown: 9 correct filters, 1 mixed, 0 missed opportunities.**

- **JOBY, RDW, CGNT, BTG** — each printed its day's high *before* the gate ever saw it (mostly at/near the 09:30 open), then faded 4–6% into the close. The gate was blocking stocks that were already rolling over by the time it looked at them.
- **GDYN, RWAY, TJGC, INFQ, NXE** — marginal cases: the day's eventual high came slightly above the block price, but only by a few cents, and every one of them gave that back and closed at or below the block price. Nothing real was left on the table.
- **SRAD is the one worth reading closely** — a 14:37 block came right as the pipeline hit `state=BREAKOUT_CONFIRMED`, and price did keep running for another 15–30 minutes (day high printed 2–9 min after the last block). But it reversed hard afterward and closed at $12.82, below every one of the 14:37+ block prices — so even that "miss" would have ended red if held to the close. Worth flagging because it's the only case where the gate's caution had a real opportunity cost, even though it wasn't wrong by end of day.

The page has the full per-symbol chart (real 1-min price line, session open, day's high, and every block marked) plus a complete table of all five component scores at each individual rejection — the "what the bot was seeing" detail you asked for.</result>
<usage><subagent_tokens>189951</subagent_tokens><tool_uses>16</tool_uses><duration_ms>342084</duration_ms></usage>
</task-notification>

## Assistant

Verified the artifact — it's built correctly: real Yahoo 1-minute price data driving the sparkline charts, actual log-derived timestamps/scores in the per-event tables, no placeholder values. Here's the review:

## [Extension Gate Review](https://claude.ai/code/artifact/0a262866-e409-4eb0-8df0-239beb9834b4)

**Verdict: 9 correct filters, 1 mixed, 0 missed opportunities** — across all 93 rejection events for the 10 symbols the new "extended from open" gate blocked today.

| Symbol | Blocks | Verdict | What the chart showed |
|---|---|---|---|
| JOBY | 20 | Correct filter | High ($6.81) printed 09:32, already down to $6.60–$6.62 by the time the gate saw it (12:33–12:54), closed -5.2% |
| RDW | 16 | Correct filter | Same shape — high at 09:32, blocked well off that high, closed -6.1% |
| SRAD | 14 | **Mixed** | Blocked at 14:37 right as pipeline read `BREAKOUT_CONFIRMED`; price *did* run another 15–30 min, but reversed and closed below every block price anyway |
| GDYN | 10 | Correct (marginal) | Closed -1.8%, day's high barely above block price and given back |
| RWAY | 8 | Correct (marginal) | Closed -0.7%, same pattern |
| CGNT | 7 | Correct filter | Closed -4.2% |
| TJGC | 5 | Correct filter | Blocked at 15:08, faded into the close |
| INFQ | 5 | Correct filter | Closed -4.4% |
| NXE | 4 | Correct filter | Closed -0.4% |
| BTG | 4 | Correct filter | Closed -0.6% |

**The one worth reading closely is SRAD**: it's the single case where the gate's caution had a real (if short-lived) opportunity cost — but even holding through the continuation, the stock reversed and closed red relative to the block prices, so the gate still wasn't *wrong* by end of day, just early to call it dead.

**Bottom line:** on today's evidence, the extension-from-open gate is doing exactly what it was built for — every other blocked symbol had already peaked (usually right at the 09:30 open) before the gate ever saw it, meaning it's mostly catching stocks on their way down, not stocks about to run. Zero cases cost the account a real missed winner today.

The artifact has the full per-symbol intraday chart with every block point marked, plus the complete score breakdown (trend/structure/prediction/entry/risk) for all 93 individual events if you want to dig into any specific one.

## User

what time srad whas selected by the scanner and became top_5

## Assistant

Confirmed `entry_shortlist_size = 5` — that's the "top 5" you're asking about (the ranked group actually evaluated for entry each cycle, distinct from the 20-symbol watchlist or the 15-symbol `final_10`).

**SRAD's timeline today:**

| Time | Event |
|---|---|
| **11:43:14** | First scored by the scanner in the broader universe pass, score=39.31 — this was just the ~356-symbol scoring sweep, not yet in the actively-monitored pool |
| **12:13:15** | Score jumped to 61.39 on rescan; SRAD entered the monitored `premarket_20` watchlist with `health=WATCH`, ranked ~10th of the ~20 candidates shown in that rotation |
| **12:30:03** | First `[PIPELINE]` evaluation logged for SRAD — this is the earliest direct evidence it cracked the **top-5 entry shortlist**, since the pipeline only evaluates shortlisted symbols |

**One honest caveat**: the exact shortlist composition (`[ENTRY] Shortlist this cycle: ...`) is logged at DEBUG level, and today's run logs at INFO — so that line was never written to `sip_bot_2026-09-09.log`. I can't show you the literal "SRAD entered the top 5 at HH:MM:SS" line; **12:30:03** is inferred as the earliest possible moment (the first cycle it got evaluated at all), which is the best evidence available without DEBUG logging on.

If you want this to be directly observable going forward (not just inferred), the fix is trivial: bump `logging.level` from `INFO` to `DEBUG` in `config.json`, or promote that one line to `log.info(...)` — happy to make that change if you want SRAD-style questions answerable exactly next time.

## User

what was the parameters of Srad at 12:54

## Assistant

SRAD doesn't have a log entry at 12:54 — it was in a `STALE` health state at that time (last transition was `WATCH → STALE` at 12:50:07, and its next `[PIPELINE]` reading isn't until 14:37), so the bot wasn't evaluating it at all at 12:54. That timestamp belongs to **JOBY**, not SRAD, in both the raw log and the artifact — likely where the mix-up came from.

**JOBY's actual parameters at 12:54** (two readings that minute):

| Time | Ext. vs open | Regime | Trend | Structure | Prediction | Entry | Risk | Price |
|---|---|---|---|---|---|---|---|---|
| 12:54:12 | 0.53% | STRUCTURE_BREAKDOWN | 50 | 75 | 15.6 | 0 | 46.13 | $6.595 |
| 12:54:17 | 0.38% | STRUCTURE_BREAKDOWN | 82.5 | 75 | 14.68 | 0 | 40.13 | $6.595 |
| 12:54:22 | 0.53% | STRUCTURE_BREAKDOWN | 50 | 75 | 15.29 | 20 | 47.19 | $6.595 |
| 12:54:27 | 0.38% | STRUCTURE_BREAKDOWN | 82.5 | 75 | 14.5 | 0 | 40.25 | $6.595 |
| 12:54:32 | 0.46% | STRUCTURE_BREAKDOWN | 72.5 | 75 | 14.84 | 20 | 44.34 | $6.600 |

(All scores measured against a required 100 — full-confirmation tier, since JOBY was >0.3% above open.)

Want SRAD's **nearest actual readings** instead — 12:49:47/12:49:52 (its last live pipeline evaluation before going STALE) — or JOBY's, since that's what actually happened at 12:54?

## User

if you review yahoo chart for srad you will see that at 11:55 symbol has a reversal, and and 11:13 another small l/h and them started to trail all the way to 13.10 I need you to identify those positions in the chart cross reference with logs, anem started to trail all the way up to $13.10, I want you to cross chart positions with the bot logs and identify why the bot didnt entered

## Assistant

Good catch — this turned up something new. Here's the full cross-reference.

## Chart pivots (real Yahoo 1-min data, 2026-09-09)

| Time | Price | What happened |
|---|---|---|
| 09:30 | $12.69 | Session open |
| 09:55 | $12.385 | Morning low (drifted down from open) |
| 11:01 | $12.69 | Local high — round-tripped back to the open, then rolled over |
| **11:13** | $12.575 | The small low/high you flagged — a brief consolidation pause partway down the 11:01→11:55 slide, not a real turn, just a pause before continuing lower |
| **11:55** | $12.44 | **The actual reversal** — day's intraday low. Confirmed by the very next bar: 11:58 jumps from $12.46 to $12.5509 on 21,293 shares (vs. ~1-3k/min all morning) — that's the real ignition bar |
| 14:41–14:42 | $13.135 | Day high (+5.6% off the 11:55 low) |
| 15:59 (close) | $12.82 | Faded off the high but still +3.1% above the 11:55 low |

## Bot state at each pivot

| Time | Chart event | What the bot was doing |
|---|---|---|
| 09:30–11:55 | Open → downdrift → reversal low | **Not watching SRAD at all.** It wasn't in `premarket_20` yet — first scored at 11:43:14 (39.31, too low), didn't crack the monitored pool until the 12:13:15 rescan (61.39). The entire reversal happened in a blind spot. |
| 11:58–12:13 | Ignition bar → price already $12.55–$12.61 | Still not in pool (added at 12:13:15, price $12.61 — already +1.4% off the 11:55 low before the bot even saw it) |
| 12:13–12:30 | Price $12.61→$12.66 | In the watchlist now, but not yet in the top-5 entry shortlist — health-ranking delay. First `[PIPELINE]` reading: 12:30:03 |
| 12:30–12:50 | Price $12.65→$12.72 | Evaluated repeatedly, blocked by ordinary gates: `structure_score=0` (no swing pivot recognized yet — the same warmup gap flagged in the Phase-1 audit), `prediction_score` 15–40 (< min 40), `risk_score` 28–38 (< min 45), reward/risk ratio 0.00–0.19 (< min 1.5) |
| 12:50–14:37 | Price runs $12.72→~$13.00+ | **Health went STALE and stayed there — zero `[PIPELINE]` evaluations for ~1h47m**, right through the bulk of the move |
| 14:37–14:41 | Price $12.97→$13.13 (the final push to the high) | Re-evaluated: state correctly read `BREAKOUT_ATTEMPT`→`BREAKOUT_CONFIRMED`, winner_score 65–76 — but blocked by the extension-from-open gate (all 5 scores need 100) and reward/risk still only 0.11–0.44 |
| 14:41:34 | Right near the exact high | Last bench event of the day — never evaluated again before the close |

## Why it never entered — and a bug I found doing this

Three compounding things, in order of impact:

1. **Discovery lag**: the bot missed the entire setup — the 11:55 reversal and the first leg up were invisible because SRAD wasn't in the candidate pool yet (13→18 min lag after the actual turn).
2. **The STALE gap**: no evaluation at all from 12:50 to 14:37 — the bot wasn't even looking during most of the actual trend.
3. **The extension gate's baseline is wrong for this symbol** — and this is a real bug, not the gate working as designed. `stream_features.py:287` computes `session_open = bars[0]["o"]`, with a comment claiming "`bars[0]` is always the session's first 1-min bar... never rolls off." That's false for SRAD: it was added to the stream mid-day (`stream.add_symbols()` never backfills history), and the bot *also restarted at 12:27:35* to deploy that very gate — which wipes the in-memory buffer clean. So SRAD's "session open" for the extension check was actually ~$12.655 (the price at 12:27–12:29, right after restart), not the real $12.69 market open. That's why the logged extension was only 0.33–0.44% at 12:49 and 2.5–3.7% by 14:41, when measured against the *true* open SRAD was already up far more than that the whole time. The gate was reading the wrong baseline — it happened to still block SRAD today (reward/risk carried that), but the anti-chase logic itself wasn't measuring what it thinks it's measuring.

This bug isn't SRAD-specific — it affects **every symbol added to the pool after the process starts, and every symbol at all after any intraday restart** (today restarted twice). Want me to fix it (have `stream.add_symbols()` backfill each new symbol's real session-open via a REST call to Alpaca, so `bars[0]` is trustworthy again), or just flag it in memory for now?

## User

fix it

## Assistant

Now let's add a public helper for today's session-open datetime:

## Assistant

Now the actual fix in `stream.py` — backfill each symbol's buffer from the real session open before it starts receiving live ticks:

## Assistant

Now update the now-accurate comment in `stream_features.py`:

## Assistant

Now let's verify: syntax, existing test suite, and a live functional check of the backfill logic itself.

## Assistant

Fixed and verified with real data — `bar[0]["o"]` for SRAD now correctly resolves to $12.69 (the true 09:30 session open), matching Yahoo Finance exactly, instead of whatever price happened to exist when the buffer was created.

## What changed

1. **`market_time.py`** — added `session_open_dt()`, a small public helper returning today's market-open as a tz-aware datetime (factored out of `minutes_since_open()`, which now reuses it).
2. **`stream.py`** — added `StreamManager._backfill()`: before any symbol's live subscription starts (both the initial `start()` and later `add_symbols()` calls), it pulls that symbol's real 1-min bars since market open via Alpaca's REST API (`alpaca_client.get_minute_bars()`, already existed, just unused for this) and pre-loads them into the buffer. Live streaming then just continues appending from "now" onward. No-op before the market actually opens (nothing to backfill yet — behavior there is unchanged).
3. **`stream_features.py`** — updated the comment that made the now-fixed false claim, so it documents the actual guarantee instead of an assumption.

## Verification

- Real REST call against SRAD confirms `bars[0]` is now `$12.69` at `09:30 ET` — the true open — with 363 bars loaded before any live tick would arrive.
- Full test suite: 70/72 pass, same 2 pre-existing unrelated failures as before (`test_entry_shortlist_starvation`, `test_simulation_e2e` — both in legacy `entry_engine.py` paths this change never touches). Zero regressions.

## What this fixes going forward

Every symbol added to the pool mid-day (rotation/replacement) or subscribed after any intraday restart will now have a correct `extension_from_open_pct` — the anti-chase gate will measure real distance from the actual open, not from whatever price happened to exist when the process last restarted. This would have caught SRAD accurately today (its true extension was 2.3–5.6%+ all afternoon, not the artificially low 0.3–0.4% the log showed).

**Note**: the bot isn't running right now (market's closed), so this hasn't been exercised live yet — it'll take effect on the next restart/session. Want me to update the two memory records with this fix, or leave that for when it's actually validated live?

## User

how expensive will be to have all the 30 symbols assigned in the stream and getting updates for parameters evaluation on every tick ,

## Assistant

I benchmarked this directly against the real pipeline rather than estimating blind. Numbers:

**Measured cost per call (this machine, full 390-bar buffer, real SRAD data):**

| Function | Cost | Single-core capacity |
|---|---|---|
| `evaluate_entry_pipeline()` — the full 9-module chain (stream_features→trend→structure→compression→regime→setup→prediction→relative_strength→winner_score→entry_score) | **~7.0 ms/call** | ~142 calls/sec |
| `intraday_health.compute_health()` — the lighter health check | **~1.15 ms/call** | ~870 calls/sec |

**What the bot spends today:** health checks run for all ~20–30 pool symbols every 5s (`poll_interval_seconds_intraday`), and the *full* pipeline only runs for the top-5 `entry_shortlist_size` every 5s. That's ~1 full-pipeline call/sec and ~5 health calls/sec on average — roughly **7ms + 6ms = 13ms of CPU per second**, under 2% of one core. Trivially cheap.

**What "all 30, every tick" would cost:** this is the part that actually matters, and it's not really a data-cost question — Alpaca's SIP feed is a flat subscription, not metered per symbol or per message, so streaming 30 symbols instead of 20 doesn't add marginal $ cost on the data side. The cost is **compute**, and it scales with tick *rate*, not symbol *count* alone:

- I don't have exact SIP trade-print rates on hand, but going by the volume bursts we've already seen today (JOBY/SRAD printing 20k–190k shares in single minute bars), a genuinely active mover can easily produce several trade+quote events per second during a breakout. Multiply that across 30 symbols simultaneously — which is exactly when this matters most, since that's the moment you'd want fresh reads — and you can plausibly exceed **142 calls/sec**, i.e. saturate a full core, on the busiest minutes of the day. Quiet symbols cost nothing; the risk concentrates exactly at the moments the bot cares about most.
- At that point "every tick" stops being real-time anyway — calls queue up behind the single-threaded poll loop, and you're evaluating stale ticks, not fresher ones. You'd be paying the cost without getting the benefit.

**The bigger issue, independent of raw cost:** the pipeline's own logic reasons over 1-minute bars (RSI/ATR windows, swing-pivot structure, regime classification) — `bars[-1]` only actually changes once a minute closes. Most individual ticks land inside a still-forming bar and would recompute to a near-identical answer. Running the full 7ms pipeline on every tick would spend most of that CPU re-deriving the same result, not new information.

**What I'd actually recommend instead of literal per-tick eval:** keep tick ingestion as-is (already O(1) per tick — `on_trade`/`on_quote` just update the rolling buffer, essentially free) for all 30 symbols, but trigger the *heavy* pipeline on **bar-close events** (once/minute/symbol, a hard ceiling of 30 calls/min = 0.5 calls/sec even with all 30 active — cheaper than today's shortlist-only 5s polling) or a throttled cadence, rather than raw tick count. That gets you full-pool coverage without the tick-burst risk, and it's cheap: 30 calls/min × 7ms ≈ 210ms/min of CPU, effectively free.

Want me to implement bar-close-triggered full-pipeline evaluation for all 30 pool symbols (replacing the top-5-shortlist-only design), or were you asking more generally before deciding whether to pursue this?

## User

I want a list of all kinds of logs the bot do

## Assistant

Here's the full catalog — 20 distinct tagged log categories across the codebase, plus a catch-all for untagged errors.

## Startup & session lifecycle
| Tag | Source | What it records |
|---|---|---|
| `[START]` | monitor.py | Process start, mode (paper/live) |
| `[STARTUP]` | intraday_health.py, position_manager.py | One-time cleanup at boot (pruning stale health-state entries from a prior day) |
| `[SHUTDOWN]` | monitor.py | Signal received, graceful shutdown, cleanup complete |
| `[EOD]` | monitor.py | End-of-day force-liquidation + the daily summary line (`trades=X wins=Y losses=Z total_P/L=$Z`) |

## Candidate discovery & filtering
| Tag | Source | What it records |
|---|---|---|
| `[PREMARKET]` | premarket_scanner.py, monitor.py | Universe filtering counts, per-symbol scores, "Selected N candidates" |
| `[FILTER]` | symbol_filters.py | Per-symbol rejection during ETF/leveraged/name-complexity filtering (DEBUG level) |
| `[INTRADAY-RESCAN]` | monitor.py | The 30-min full rescans — pool rotation, per-symbol score+health snapshot |
| `[REPLACEMENT]` | top_stocks.py | Finding a replacement candidate when a position slot opens |
| `[OPEN]` | monitor.py | Market-open transition / stream-start warnings |

## Health & entry evaluation
| Tag | Source | What it records |
|---|---|---|
| `[HEALTH]` | intraday_health.py | State transitions (e.g. `WATCH -> STALE`), score, price/vwap slope |
| `[ENTRY]` | monitor.py, position_manager.py | Shortlist benching ("benched after 5 consecutive not-confirmed attempts"), slot availability |
| `[SLOT]` | monitor.py | Open position slot available |
| `[CONFIRMATION]` | entry_engine.py | Legacy engine's confirm/reject reasoning (only active when `decision_engine: "legacy"`) |
| `[PIPELINE]` | monitor.py | New engine's confirm/reject reasoning — state, winner_score, all five component scores + reasons (only when `decision_engine: "prediction_pipeline"`) |

## Position management
| Tag | Source | What it records |
|---|---|---|
| `[STOP]` | position_manager.py | Initial stop set on entry |
| `[TRAIL]` | position_manager.py | Trailing-stop updates, grace extensions on a stop touch |
| `[EXIT]` | position_manager.py | Close order submission/fill, or a warning if one's already resting |
| `[EXIT_REASON]` | position_manager.py | Why (`TRAILING_STOP`, `END_OF_DAY`, etc.) |
| `[P/L]` | position_manager.py | Realized profit/loss for that trade |
| `[RECONCILE]` | position_manager.py | Reconciling local state against the broker's actual positions (throttled to every 30s) |

## Infrastructure
| Tag | Source | What it records |
|---|---|---|
| `[STREAM]` | stream.py | SIP connect/reconnect, backfill success/failure (the fix from earlier) |

## Untagged (catch-all)
~15% of log calls have no bracket tag — mostly raw exception messages from `alpaca_client.py` (`get_snapshots failed`, `get_minute_bars(...) failed`, order/position API failures) and config-fallback warnings (`Unknown feed '...' in config`). These are lower-frequency and typically only appear on an actual API hiccup or a config typo.

**One gap worth knowing**: everything above logs at `INFO` (or `DEBUG`/`WARNING`/`ERROR` as noted) except `[FILTER]`, which is `DEBUG`-only — and the config's `logging.level` is currently `"INFO"`, so `[FILTER]` lines (and the `[ENTRY] Shortlist this cycle` line we talked about earlier, which isn't tagged separately but lives in the same `[ENTRY]` family) never actually reach `sip_bot_*.log` today.

## User

can you save it in the folder docs as a pdf

## Assistant

Saved and verified — renders cleanly across 2 pages.

**`docs/bot_log_catalog.pdf`** — the full catalog of all 20 tagged log categories (`[START]`, `[PREMARKET]`, `[HEALTH]`, `[PIPELINE]`, `[ENTRY]`, `[TRAIL]`, `[EXIT]`, `[P/L]`, etc.), grouped by lifecycle stage (startup, discovery/filtering, health/entry evaluation, position management, infrastructure), each with its source file and what it records — plus the untagged-error catch-all and the `[FILTER]`/DEBUG-level logging gap noted at the bottom.

## User

also create a pdf with a list of all the parameters used to select the top_30 watchlist and the parameters for the entry gate

## Assistant

Now let's get symbol_filters.py's exclusion rules and the full entry_score gate config:

## Assistant

Renders cleanly across 3 pages, all values correct against the live `config.json`. Saved:

**`docs/watchlist_and_entry_gate_parameters.pdf`** — two sections:

1. **Top-30 watchlist selection** — universe filters (price band, volume floor, OTC/ETF/leveraged/name-complexity exclusions), `scorer.py`'s scoring thresholds, and the full weight breakdown (volume family carries the most weight at 34 combined, down to the 10-point extension-risk penalty).
2. **Entry gate** — the active `entry_score.py` gate (all five score floors, the confirmation window, and the extension-from-open hard tier we built together), plus the dormant legacy `entry_engine.py` gate listed separately and clearly marked inactive, including the RSI-overbought check it has that the active engine still lacks.

