scan.py: real bid-depth liquidity flag, real-variance flow bound, profit/area

- max_units_at_impact(): walks the LIVE bid book for a genuine, ticker-
  specific instant-exit liquidity check, replacing a flat 25%-of-flow
  guess (a price-vs-volume elasticity regression was tried first and
  abandoned - confounded by demand-driven volume on real data). Surfaced
  as an [exit-thin] WARNING flag, not a hard cap - a single order-book
  snapshot is less reliable than the 30-day flow aggregate that still
  drives n_lim/filtering (using it as a hard cap once silently zeroed
  out a real recipe from one noisy snapshot).
- tref_precise(): real variance of daily traded volume instead of a
  Poisson unit-independence bound, wired into stage 2 only (bounded
  candidate set, cached). Also fixed: was averaging only over trading
  days, overstating flow ~3x for sparse tickers.
- n_out(): dropped the demand-coverage term, which penalized thin
  resting-bid books even though production sells via patient asks taken
  by walk-up buyers with no resting bid at all.
- --sort area: profit/day per tile of TOTAL footprint, now including the
  single-tier hab area a building's workforce drags along (was building
  area only).
- --deprec/--reclaim-frac: amortizes unreclaimed capex, not full capex -
  real FIO reclaim data showed 50-86% returned even at near-100%
  condition, contradicting a 100%->0 decay-curve assumption.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-10-10 23:06:49 +02:00
co-authored by Claude Sonnet 5
parent 11f54276b3
commit 3f67cc7ed5
3 changed files with 135 additions and 33 deletions
+24 -5
View File
@@ -1,3 +1,4 @@
import datetime
import math
from puga import saturation as s, market, fio
@@ -8,12 +9,22 @@ def test_tref_uses_min_and_poisson_floor():
assert s.tref(0, 0) == 0
def test_n_out_penalises_queue_and_thin_demand():
free = s.n_out(100, 0, 10_000, 5) # no queue, ample demand
def test_tref_precise_pads_missing_calendar_days_not_just_trading_days(monkeypatch):
# 10 trading days out of 30, each with volume 30 -> real per-calendar-day mean is 10, not 30.
# The original bug averaged only over the 10 trading-day rows (mean 30), overstating the
# bound 3x for exactly the sparse tickers it's meant to protect.
base = datetime.date(2026, 1, 1)
fake_td = [(base + datetime.timedelta(days=i * 3), 30.0) for i in range(10)]
monkeypatch.setattr(s, "trading_days", lambda tk, cx, days: fake_td)
bound = s.tref_precise("FAKE", "AI1", traded7=30, traded30=30, days=30)
assert bound < 15.0 # well below the naive trading-day mean of 30, reflecting real sparsity
def test_n_out_penalises_queue():
free = s.n_out(100, 0, 5) # no queue
assert math.isclose(free, 0.25 * 100 / 5)
assert s.n_out(100, 7000, 10_000, 5) < free # 70 days of queue cuts it
assert s.n_out(100, 0, 100, 5) < free # demand only 1 day of flow cuts it
assert s.n_out(0, 0, 100, 5) == 0
assert s.n_out(100, 7000, 5) < free # 70 days of queue cuts it
assert s.n_out(0, 0, 5) == 0
def test_thin_flag():
@@ -57,3 +68,11 @@ def test_effective_demand_excludes_stale_bids_below_the_band():
bids = [(4550, 8), (4200, 250), (2810, 8), (334, 19)]
assert s.effective_demand(bids, band=(663, 16575)) == 8 + 250 + 8 # the 334 bid is stale, excluded
assert s.effective_demand(bids, band=None) == 8 + 250 + 8 + 19
def test_max_units_at_impact_stops_at_the_price_threshold():
bids = [(100, 10), (95, 20), (85, 1000)] # ref 100, 10% floor = 90
assert s.max_units_at_impact(bids, 100, max_impact_pct=10.0) == 30 # 100 and 95 tiers only
assert s.max_units_at_impact(bids, 100, max_impact_pct=20.0) == 1030 # all three tiers now qualify
assert s.max_units_at_impact([], 100) == 0.0
assert s.max_units_at_impact(bids, 0) == 0.0