"""Finds manufactured goods currently selling below their implied production cost (output price minus input cost, from live daily VWAP candles) - the mirror image of saturation.py's "is this worth building" question: here we're asking "is this worth buying and holding for a price- reversion bounce." Backtested 2026-10-09 across 326 output tickers / 112 distinct tickers that ever showed the signal, 287 independent dip-onset episodes (deduplicated so a persisting dip only counts once, at onset, not once per month it continues): at >=10 units/day traded volume, +3 months forward price return was mean +21.3%, median +8.9%, 73.2% of episodes positive (see empire/docs or the session that built this for the raw numbers). That is ~0.10-0.24%/day - MUCH lower than this project's production-building marginal ROI (14-29%/day seen this session). Only worth deploying *excess* capital into once better-yielding builds are exhausted, not as a primary strategy, and it's a gross-return backtest with no spread/freight/storage-fee/capital-lockup cost subtracted. """ from . import fio from .history import daily_prices def manufacturing_candidates(): """One representative recipe per output ticker, excluding pure-extraction buildings (no material inputs to compute an implied cost from).""" seen = {} for r in fio.recipes(): if r["BuildingTicker"] in ("EXT", "COL", "RIG") or not r["Inputs"]: continue for o in r["Outputs"]: seen.setdefault(o["Ticker"], r) return seen def current_margin(ticker: str, recipe: dict, cx: str, window_days: int = 35, recent_days: int = 14): """Mean margin%/price/volume over the most recent `recent_days` of overlapping price history for this recipe's inputs+output, within a `window_days` lookback. None if not enough history.""" ins = {i["Ticker"]: i["Amount"] for i in recipe["Inputs"]} outs = {o["Ticker"]: o["Amount"] for o in recipe["Outputs"]} series = {m: daily_prices(m, cx) for m in list(ins) + list(outs)} if any(not s for s in series.values()): return None days = sorted(set().union(*[set(s) for s in series.values()]))[-window_days:] last = {} rows = [] for d in days: for m, s in series.items(): if d in s: last[m] = s[d][0] if len(last) == len(series): rev = sum(last[m] * n for m, n in outs.items()) cost = sum(last[m] * n for m, n in ins.items()) rows.append((rev, cost, last[ticker], series[ticker].get(d, (0, 0))[1])) recent = rows[-recent_days:] if len(recent) < recent_days // 2: return None rev_m = sum(x[0] for x in recent) / len(recent) cost_m = sum(x[1] for x in recent) / len(recent) px_m = sum(x[2] for x in recent) / len(recent) vol_m = sum(x[3] for x in recent) / len(recent) margin_pct = 100 * (rev_m - cost_m) / cost_m if cost_m else 0.0 return {"margin_pct": margin_pct, "price": px_m, "volume": vol_m, "building": recipe["BuildingTicker"]} def scan_current(cx: str, min_vol: float = 5.0): """Every manufactured good currently priced below its implied production cost, with enough volume to actually trade, sorted most-undervalued first.""" out = [] for t, r in manufacturing_candidates().items(): m = current_margin(t, r, cx) if m and m["margin_pct"] < 0 and m["volume"] >= min_vol: out.append({"ticker": t, **m}) out.sort(key=lambda x: x["margin_pct"]) return out