delete: dead DDL (10 tables), dead projections, pinned dead code; README/pyproject prune

pg_schema.sql: drop CREATE TABLE for 10 zero-reference tables (operator DROP
list in plans/002-deletions.log — production tables untouched until run).
projections.py: remove 4 zero-consumer query functions.
Clear all 'plan 002' ruff pins from the CI gate (files now F-clean);
api/main.py:F821 pin remains for plan 007. README repo-map/test-count fixed;
pydantic-settings removed (zero imports). Full receipts: plans/002-deletions.log

Suite: 351 passed (16 schwab/dead-module tests removed), 15 failures = the
known WSL-loopback dashboard set only.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
momentum devandClaude Fable 5 committed 2026-07-13 21:09:35 -04:00
1 parent cf431c6c2b
commit af8137dfd7
11 files changed
+53 -234

No files matched your search

-9
View File
@@ -39,20 +39,11 @@ jobs:
# F errors anywhere in scope still fail; remove each pin when its owning # F errors anywhere in scope still fail; remove each pin when its owning
# plan lands: # plan lands:
# api/main.py F821 (missing os/hmac imports) -> plan 007 # api/main.py F821 (missing os/hmac imports) -> plan 007
# research/gapper_history.py F401, research/labeler.py F541/F841,
# storage/event_store.py F401,
# strategy/evaluation/structure.py F401/F841 (dead code) -> plan 002
- run: >- - run: >-
ruff check --select F ruff check --select F
run_live_paper.py trading_execution.py telegram_bot.py run_live_paper.py trading_execution.py telegram_bot.py
api/ alpaca_paper/ runtime/ storage/ strategy/ research/ api/ alpaca_paper/ runtime/ storage/ strategy/ research/
--per-file-ignores "api/main.py:F821" --per-file-ignores "api/main.py:F821"
--per-file-ignores "research/gapper_history.py:F401"
--per-file-ignores "research/labeler.py:F541"
--per-file-ignores "research/labeler.py:F841"
--per-file-ignores "storage/event_store.py:F401"
--per-file-ignores "strategy/evaluation/structure.py:F401"
--per-file-ignores "strategy/evaluation/structure.py:F841"
- run: python -m pytest -q - run: python -m pytest -q
env: env:
DATABASE_URL: postgresql://momentum:momentum@localhost:5432/momentum DATABASE_URL: postgresql://momentum:momentum@localhost:5432/momentum
+3 -4
View File
@@ -110,8 +110,8 @@ The `.sh` wrappers set the venv + cwd so cron can call them directly:
```bash ```bash
DATABASE_URL=postgresql://user:pass@host:5432/momentum python -m pytest DATABASE_URL=postgresql://user:pass@host:5432/momentum python -m pytest
# ~130 tests: strategy, storage, triggers, execution (cooldown/re-entry/EOD # ~365 tests: strategy, storage, triggers, execution (cooldown/re-entry/EOD
# flatten/naked-stop guards), exits, journal P&L, schwab, api, research. # flatten/naked-stop guards), exits, journal P&L, api, research.
``` ```
Tests isolate via throwaway `mem_*` Postgres schemas; `tests/conftest.py` sweeps Tests isolate via throwaway `mem_*` Postgres schemas; `tests/conftest.py` sweeps
@@ -180,7 +180,6 @@ strategy/ pure strategy engine: evaluation, structure, exits, backte
storage/ event schema + store (PostgreSQL), projections storage/ event schema + store (PostgreSQL), projections
research/ research DBs, ingestion (bars, RSS, gappers), screeners research/ research DBs, ingestion (bars, RSS, gappers), screeners
alpaca_paper/ Alpaca paper client, executor, account/order sync alpaca_paper/ Alpaca paper client, executor, account/order sync
schwab/ Schwab OAuth, market data, positions/orders, health
api/ + dashboard_api.py dashboard JSON API + static UI (:8765) api/ + dashboard_api.py dashboard JSON API + static UI (:8765)
admin_api.py admin panel (:8767): health, config overrides, profiles, restarts admin_api.py admin panel (:8767): health, config overrides, profiles, restarts
youtube_claims/ trader-video transcript -> claim extraction pipeline youtube_claims/ trader-video transcript -> claim extraction pipeline
@@ -193,7 +192,7 @@ tests/ pytest suite (unit + integration)
## Security ## Security
`.env` and all of `data/` (DBs, `data/schwab_tokens.json`) are git-ignored and `.env` and all of `data/` are git-ignored and
never committed; token files are written `0600`. The `.env` holds **live broker / never committed; token files are written `0600`. The `.env` holds **live broker /
database / webhook credentials** — keep it local. If any secret was ever database / webhook credentials** — keep it local. If any secret was ever
committed to a repo's history, **rotate it** (re-issue Alpaca keys / re-run the committed to a repo's history, **rotate it** (re-issue Alpaca keys / re-run the
+47
View File
@@ -0,0 +1,47 @@
# Plan 002 deletions log — 2026-07-13, branch milestone-2026-07
Every deletion was grep-gated: `grep -rn "<name>" --include="*.py" --include="*.yml" --include="*.sh"` excluding the target itself, plans/, docs/. "clear" = zero live references.
## Deleted (grep receipts)
- schwab/ (23 tracked files) — clear (README prose + .env.example keys only; both cleaned). Commit message of d54a2a9 claimed this deletion; it never happened.
- tests/unit/schwab/ — tests of the deleted module.
- milestone3_verify.py — schwab-only walk-through; clear.
- comprehensive_backtest.py — crashes on import (research/connection.py absent); clear.
- strategy/universe.py + UniverseConfig — clear.
- strategy/backtest/outcomes.py (+ its exports in strategy/backtest/__init__.py) — clear.
- detect_momentum_ignition in strategy/evaluation/ignition.py — clear; **kept `_last_higher_low`** (imported by runner.py:33, premarket_setup.py:32, liquid_breakout.py:44). tests/unit/test_ignition.py trimmed to the kept helper.
- grafana/ (standalone stack) — pointed at decommissioned pre-migration DB. **Provisioning dashboards MOVED** to deploy/compose/grafana/dashboards/ and docker-compose.yml bind-mount re-pointed (the in-compose grafana service keeps working). SECURITY NOTE: the deleted datasource YAML carried committed dev Postgres credentials (values not reproduced here); they remain in git history — rotate that DB user's password if the old 5432 instance still runs.
- telemetry/ — contained only stale __pycache__ (untracked); removed from disk.
- .github/workflows/docker-publish.yml — fired on nonexistent `main` + stale artifact branch; pushed wrong image name (momentum-system).
- uv.lock — DuckDB-era lockfile; requirements.txt is the real manifest.
- SCHWAB_* keys from .env.example.
- pydantic-settings from pyproject.toml — zero imports repo-wide.
- Dead intra-file code (the CI ruff pins annotated "plan 002", all removed and pins dropped from docker.yml): unused imports in research/gapper_history.py, storage/event_store.py, strategy/evaluation/structure.py; empty f-string + dead `orb` query in research/labeler.py; dead `closes` assignment in structure.py.
## Dead DDL removed from storage/pg_schema.sql (10 tables, zero code references)
backtest_runs, halt_events, market_context, paper_missed_trades, paper_sessions,
short_interest, simulated_fills, simulated_orders, strategy_runs, telemetry_events
## Dead projections removed from storage/projections.py (zero consumers)
query_event_timeline, query_positions_view, query_settings_snapshot, query_symbol_status
(private helpers _evt_window/_hist_window/_latest_event_payload/_state_until/_sum_position_unrealized all have internal callers — kept)
## Kept (grep hit live code, or disposition elsewhere)
- strategy/evaluation/ignition.py `_last_higher_low` — live (3 importers).
- quote_snapshots, float_cache — created by CODE (run_live_paper.py, research/ingestion/float_cache.py), no pg_schema DDL to remove; write-only today; disposition in plan 006.
- dilution_events — pg_schema DDL kept; disposition in plan 006.
## FOR THE OPERATOR — run only after plan 001's backup exists + one session soak
```sql
DROP TABLE IF EXISTS backtest_runs;
DROP TABLE IF EXISTS halt_events;
DROP TABLE IF EXISTS market_context;
DROP TABLE IF EXISTS paper_missed_trades;
DROP TABLE IF EXISTS paper_sessions;
DROP TABLE IF EXISTS short_interest;
DROP TABLE IF EXISTS simulated_fills;
DROP TABLE IF EXISTS simulated_orders;
DROP TABLE IF EXISTS strategy_runs;
DROP TABLE IF EXISTS telemetry_events;
```
These were removed from pg_schema.sql only; the production database still holds the (empty/stale) tables until you run the DROPs.
+1 -1
View File
@@ -17,7 +17,7 @@ your row when done.
| Plan | Title | Priority | Effort | Depends on | Status | | Plan | Title | Priority | Effort | Depends on | Status |
|------|-------|----------|--------|------------|--------| |------|-------|----------|--------|------------|--------|
| 001 | Production safety rails — CI test gate, dead-man's switch, pg_dump backup | P1 | S | — | DONE | | 001 | Production safety rails — CI test gate, dead-man's switch, pg_dump backup | P1 | S | — | DONE |
| 002 | Dead-weight deletion — schwab/, grafana/, dead modules/tables/projections | P2 | S | 001 | TODO | | 002 | Dead-weight deletion — schwab/, grafana/, dead modules/tables/projections | P2 | S | 001 | DONE |
| 003 | Money-path hardening — daily self-restart, positive-evidence contract, reconcile, cage count | P1 | M | 001 | TODO | | 003 | Money-path hardening — daily self-restart, positive-evidence contract, reconcile, cage count | P1 | M | 001 | TODO |
| 004 | Learning flywheel — one population module, scheduled eod_replay + nightly_tune, atomic writes | P1 | M | 001 (002 rec.) | TODO | | 004 | Learning flywheel — one population module, scheduled eod_replay + nightly_tune, atomic writes | P1 | M | 001 (002 rec.) | TODO |
| 005 | Entry-path unification + decision receipts | P2 | M | 001, 003, 004 | TODO | | 005 | Entry-path unification + decision receipts | P2 | M | 001, 003, 004 | TODO |
-1
View File
@@ -11,7 +11,6 @@ dependencies = [
"psycopg2-binary>=2.9.0", "psycopg2-binary>=2.9.0",
"pandas>=2.0.0", "pandas>=2.0.0",
"pydantic>=2.0.0", "pydantic>=2.0.0",
"pydantic-settings>=2.0.0",
"pyyaml>=6.0", "pyyaml>=6.0",
"typer>=0.9.0", "typer>=0.9.0",
"rich>=13.0.0", "rich>=13.0.0",
+1 -1
View File
@@ -24,7 +24,7 @@ market at minute resolution:
import argparse import argparse
import time import time
from collections import defaultdict from collections import defaultdict
from datetime import date, datetime, timedelta from datetime import datetime, timedelta
import os import os
import pandas as pd import pandas as pd
+1 -7
View File
@@ -774,12 +774,6 @@ def validate_runner(con):
lb_1r = _wilson_lower(sum(r1), len(r1)) lb_1r = _wilson_lower(sum(r1), len(r1))
fresh = [r[2] for r in rows if r[3] is None or r[3] <= RUNNER_PM_EXHAUSTION_FRAC] fresh = [r[2] for r in rows if r[3] is None or r[3] <= RUNNER_PM_EXHAUSTION_FRAC]
rate_fresh = (sum(fresh) / len(fresh)) if fresh else 0.0 rate_fresh = (sum(fresh) / len(fresh)) if fresh else 0.0
orb = con.execute(
"WITH rn AS (SELECT DISTINCT symbol, session_date FROM setup_events WHERE setup_version=?) "
"SELECT avg(CASE WHEN l.reached_1r_before_minus_1r THEN 1.0 ELSE 0 END), count(*) "
"FROM setup_events se JOIN outcome_labels l ON l.setup_id = se.setup_id "
"JOIN rn ON rn.symbol = se.symbol AND rn.session_date = se.session_date "
"WHERE se.setup_version=?", [RUNNER_SETUP_VERSION, SETUP_VERSION]).fetchone()
vr = con.execute( vr = con.execute(
"WITH rn AS (SELECT DISTINCT symbol, session_date FROM setup_events WHERE setup_version=?) " "WITH rn AS (SELECT DISTINCT symbol, session_date FROM setup_events WHERE setup_version=?) "
"SELECT avg(CASE WHEN l.reached_1r_before_minus_1r THEN 1.0 ELSE 0 END), count(*) " "SELECT avg(CASE WHEN l.reached_1r_before_minus_1r THEN 1.0 ELSE 0 END), count(*) "
@@ -1391,7 +1385,7 @@ def lift(con):
ct = df[df["cat_type"].notna()].groupby("cat_type", observed=True)[label].agg(["mean", "count"]) ct = df[df["cat_type"].notna()].groupby("cat_type", observed=True)[label].agg(["mean", "count"])
ct = ct[ct["count"] >= 15].sort_values("mean", ascending=False) ct = ct[ct["count"] >= 15].sort_values("mean", ascending=False)
if len(ct): if len(ct):
print(f" catalyst_type (n>=15): " + " | ".join( print(" catalyst_type (n>=15): " + " | ".join(
f"{t}:{r['mean']*100:.0f}%/{int(r['count'])}({r['mean']/base:.1f}x)" for t, r in ct.iterrows())) f"{t}:{r['mean']*100:.0f}%/{int(r['count'])}({r['mean']/base:.1f}x)" for t, r in ct.iterrows()))
print("\nCAVEAT: ~17-19 autocorrelated days, +20% runners are sparse — a lift is a HYPOTHESIS to") print("\nCAVEAT: ~17-19 autocorrelated days, +20% runners are sparse — a lift is a HYPOTHESIS to")
print("shadow-confirm forward, not a validated win-rate. Anything 'thin n' is noise until more data.") print("shadow-confirm forward, not a validated win-rate. Anything 'thin n' is noise until more data.")
-1
View File
@@ -6,7 +6,6 @@ Append-only event storage with canonical event types.
from .event_schema import ( from .event_schema import (
BaseEvent, BaseEvent,
EventType, EventType,
EventMode,
) )
from .db import get_connection from .db import get_connection
from uuid import uuid4 from uuid import uuid4
-137
View File
@@ -126,36 +126,7 @@ CREATE TABLE IF NOT EXISTS news_events (
PRIMARY KEY (id) PRIMARY KEY (id)
); );
CREATE TABLE IF NOT EXISTS halt_events (
id VARCHAR,
symbol VARCHAR,
halt_start TIMESTAMP,
halt_end TIMESTAMP,
halt_type VARCHAR,
price_at_halt DOUBLE PRECISION,
resumed_price DOUBLE PRECISION,
gap_on_resume_pct DOUBLE PRECISION,
PRIMARY KEY (id)
);
CREATE TABLE IF NOT EXISTS market_context (
timestamp TIMESTAMP,
trade_date DATE,
spy_open DOUBLE PRECISION,
spy_high DOUBLE PRECISION,
spy_low DOUBLE PRECISION,
spy_close DOUBLE PRECISION,
spy_volume BIGINT,
qqq_open DOUBLE PRECISION,
qqq_close DOUBLE PRECISION,
spy_intraday_return_pct DOUBLE PRECISION,
vix_open DOUBLE PRECISION,
vix_level DOUBLE PRECISION,
advance_decline_ratio DOUBLE PRECISION,
up_volume_down_volume_ratio DOUBLE PRECISION,
tape_regime_flag VARCHAR,
PRIMARY KEY (timestamp)
);
CREATE TABLE IF NOT EXISTS dilution_events ( CREATE TABLE IF NOT EXISTS dilution_events (
id VARCHAR PRIMARY KEY, id VARCHAR PRIMARY KEY,
@@ -168,14 +139,6 @@ CREATE TABLE IF NOT EXISTS dilution_events (
gross_proceeds DOUBLE PRECISION gross_proceeds DOUBLE PRECISION
); );
CREATE TABLE IF NOT EXISTS short_interest (
symbol VARCHAR,
report_date DATE,
short_interest_shares BIGINT,
short_interest_pct_float DOUBLE PRECISION,
days_to_cover DOUBLE PRECISION,
PRIMARY KEY (symbol, report_date)
);
-- ===================================================================== -- =====================================================================
-- Features / setups / outcomes / backtests -- Features / setups / outcomes / backtests
@@ -252,102 +215,14 @@ CREATE TABLE IF NOT EXISTS outcome_labels (
time_to_max_drawdown_minutes DOUBLE PRECISION time_to_max_drawdown_minutes DOUBLE PRECISION
); );
CREATE TABLE IF NOT EXISTS backtest_runs (
run_id VARCHAR PRIMARY KEY,
run_timestamp TIMESTAMP,
strategy_name VARCHAR,
strategy_version VARCHAR,
feature_version VARCHAR,
date_range_start DATE,
date_range_end DATE,
parameter_config_json VARCHAR,
summary_metrics_json VARCHAR,
total_trades INTEGER,
win_rate DOUBLE PRECISION,
expectancy DOUBLE PRECISION,
max_drawdown DOUBLE PRECISION,
profit_factor DOUBLE PRECISION
);
-- ===================================================================== -- =====================================================================
-- Paper trading -- Paper trading
-- ===================================================================== -- =====================================================================
CREATE TABLE IF NOT EXISTS simulated_orders (
order_id VARCHAR PRIMARY KEY,
session_id VARCHAR,
symbol VARCHAR,
created_at TIMESTAMP,
side VARCHAR,
order_type VARCHAR,
qty INTEGER,
intended_entry DOUBLE PRECISION,
stop_price DOUBLE PRECISION,
target_price DOUBLE PRECISION,
status VARCHAR,
strategy_name VARCHAR,
setup_id VARCHAR
);
CREATE TABLE IF NOT EXISTS simulated_fills (
fill_id VARCHAR PRIMARY KEY,
order_id VARCHAR,
symbol VARCHAR,
fill_time TIMESTAMP,
fill_price DOUBLE PRECISION,
qty INTEGER,
slippage DOUBLE PRECISION,
commission DOUBLE PRECISION,
venue VARCHAR
);
CREATE TABLE IF NOT EXISTS strategy_runs (
run_id VARCHAR PRIMARY KEY,
started_at TIMESTAMP,
ended_at TIMESTAMP,
mode VARCHAR,
strategy_name VARCHAR,
strategy_version VARCHAR,
config_json VARCHAR,
summary_json VARCHAR
);
CREATE TABLE IF NOT EXISTS paper_sessions (
session_id VARCHAR PRIMARY KEY,
trade_date DATE,
started_at TIMESTAMP,
ended_at TIMESTAMP,
starting_equity DOUBLE PRECISION,
ending_equity DOUBLE PRECISION,
realized_pnl DOUBLE PRECISION,
unrealized_pnl DOUBLE PRECISION,
total_orders INTEGER,
total_fills INTEGER,
total_setups INTEGER,
notes VARCHAR
);
CREATE TABLE IF NOT EXISTS paper_missed_trades (
audit_id VARCHAR PRIMARY KEY,
session_id VARCHAR,
symbol VARCHAR,
setup_id VARCHAR,
reason VARCHAR,
setup_time TIMESTAMP,
evaluated_at TIMESTAMP,
attempted_entry_time TIMESTAMP,
entry_cutoff VARCHAR,
minutes_past_cutoff DOUBLE PRECISION,
success_score_pct DOUBLE PRECISION,
criteria_passed INTEGER,
criteria_total INTEGER,
provider_status VARCHAR,
provider_degraded_count INTEGER,
source_status VARCHAR,
source_degraded_count INTEGER,
source_backoff_count INTEGER,
created_at TIMESTAMP,
details_json VARCHAR
);
-- ===================================================================== -- =====================================================================
-- Raw landing (append-only) + telemetry + scanner snapshots -- Raw landing (append-only) + telemetry + scanner snapshots
@@ -412,18 +287,6 @@ CREATE INDEX IF NOT EXISTS idx_news_catalyst_cache_symbol
CREATE INDEX IF NOT EXISTS idx_news_catalyst_cache_enriched CREATE INDEX IF NOT EXISTS idx_news_catalyst_cache_enriched
ON news_catalyst_cache (enriched_at); ON news_catalyst_cache (enriched_at);
CREATE TABLE IF NOT EXISTS telemetry_events (
id VARCHAR PRIMARY KEY,
event_type VARCHAR,
session_id VARCHAR,
symbol VARCHAR,
source VARCHAR,
from_state VARCHAR,
to_state VARCHAR,
reason VARCHAR,
metadata_json VARCHAR,
created_at TIMESTAMP DEFAULT current_timestamp
);
CREATE TABLE IF NOT EXISTS scanner_snapshots ( CREATE TABLE IF NOT EXISTS scanner_snapshots (
id VARCHAR PRIMARY KEY, id VARCHAR PRIMARY KEY,
-71
View File
@@ -103,27 +103,6 @@ def query_session_summary(store) -> list[dict]:
return summaries return summaries
def query_symbol_status(store, symbol: str | None = None) -> list[dict]:
"""Query current status for symbols."""
events = store.query_events(
event_type="symbol_state_changed",
symbol=symbol,
)
return [
{
"event_id": event["id"],
"symbol": json.loads(event.get("payload_json", "{}")).get("symbol"),
"previous_state": json.loads(event.get("payload_json", "{}")).get(
"previous_state"
),
"new_state": json.loads(event.get("payload_json", "{}")).get("new_state"),
"timestamp": event["timestamp"],
}
for event in events
]
def query_approval_queue(store) -> list[dict]: def query_approval_queue(store) -> list[dict]:
requested = store.query_events(event_type="order_approval_requested", limit=None) requested = store.query_events(event_type="order_approval_requested", limit=None)
approved = store.query_events(event_type="order_approved", limit=None) approved = store.query_events(event_type="order_approved", limit=None)
@@ -158,56 +137,6 @@ def query_approval_queue(store) -> list[dict]:
return queue return queue
def query_positions_view(store) -> list[dict]:
"""Query all positions (opened/closed events)."""
opened = store.query_events(event_type="position_opened")
closed = store.query_events(event_type="position_closed")
position_events = opened + closed
positions = []
for event in position_events:
payload = json.loads(event.get("payload_json", "{}"))
positions.append(
{
"event_id": event["id"],
"position_id": payload.get("position_id"),
"symbol": payload.get("symbol"),
"event_type": event["event_type"],
"entry_price": payload.get("entry_price"),
"quantity": payload.get("quantity"),
"stop_loss_price": payload.get("stop_loss_price"),
"exit_price": payload.get("exit_price")
if event["event_type"] == "position_closed"
else None,
"exit_reason": payload.get("exit_reason")
if event["event_type"] == "position_closed"
else None,
"realized_pnl": payload.get("realized_pnl")
if event["event_type"] == "position_closed"
else None,
"timestamp": event["timestamp"],
}
)
return positions
def query_event_timeline(store, session_id: str | None = None) -> list[dict]:
"""Query full event timeline for a session."""
return store.query_events(session_id=session_id, limit=None)
def query_settings_snapshot(store) -> dict:
"""Query current settings snapshot."""
events = store.query_events(event_type="settings_snapshot")
if not events:
return {}
latest_event = events[-1]
return json.loads(latest_event.get("payload_json", "{}"))
def query_ready_signals_snapshot(store, session_id: str | None = None) -> list[dict]: def query_ready_signals_snapshot(store, session_id: str | None = None) -> list[dict]:
"""Latest signal_ready per symbol, newest first. Feeds the dashboard.""" """Latest signal_ready per symbol, newest first. Feeds the dashboard."""
events = store.query_events( events = store.query_events(
-2
View File
@@ -7,7 +7,6 @@ based on Ross Cameron momentum strategy specifications.
import os import os
from dataclasses import dataclass from dataclasses import dataclass
from enum import Enum from enum import Enum
from typing import Literal
import numpy as np import numpy as np
import pandas as pd import pandas as pd
@@ -192,7 +191,6 @@ def detect_first_pullback(
highs = bars["high"].values highs = bars["high"].values
lows = bars["low"].values lows = bars["low"].values
closes = bars["close"].values
# Find impulse high in early session window (avoid late-day highs) # Find impulse high in early session window (avoid late-day highs)
search_end = min(len(highs), impulse_search_bars) search_end = min(len(highs), impulse_search_bars)