fix: realistic transaction costs, colorbar layout, equity curve clipping

- Costs updated to evidence-based values (SEC small-cap liquidity study 2013,
  Nasdaq spread data 2021, AQR Trading Costs paper 2018):
  large ~0.2% RT, mid ~0.5%, small ~1.5%, micro ~5%
- Micro-cap note: Alpaca does not allow new OTC/Pink Sheet positions;
  most micro-cap signals are untradeable; at realistic 5% RT, micro-cap
  destroys capital (-36% to -81% excess return)
- db.py: get_cached_market_caps returns already_fetched set including null
  rows, preventing repeated yfinance re-queries for known-missing tickers
- plot_hp_heatmap: colorbar in dedicated axes (right margin), no overlap
- plot_equity_curves: two-pass approach clips all curves to min end date
- README: updated cost table, shortened insidercopytrading.com section

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-27 14:23:13 +02:00
co-authored by Claude Sonnet 4.6
parent 9417a9e542
commit b615920843
7 changed files with 65 additions and 50 deletions
+6 -3
View File
@@ -219,14 +219,17 @@ def get_signals_for_backtest(min_score: float, min_cluster_size: int) -> list[di
return [_signal_to_dict(r) for r in rows]
def get_cached_market_caps(tickers: list[str]) -> dict[str, float]:
def get_cached_market_caps(tickers: list[str]) -> tuple[dict[str, float], set[str]]:
"""Return (cap_map, already_fetched_set). already_fetched includes tickers with null cap."""
if not tickers:
return {}
return {}, set()
with _session() as session:
rows = session.scalars(
select(TickerMeta).where(TickerMeta.ticker.in_(tickers))
).all()
return {r.ticker: r.market_cap for r in rows if r.market_cap is not None}
caps = {r.ticker: r.market_cap for r in rows if r.market_cap is not None}
fetched = {r.ticker for r in rows}
return caps, fetched
def upsert_market_caps(caps: dict[str, float]) -> None: