Commit Graph
23 Commits
Author SHA1 Message Date
dodoxandClaude Sonnet 5 bf15c709ea Add planet environment-extra materials to capex calcs; buildcost.py tool
Every building needs a planet-hazard material bundle on top of its normal
BuildingCosts (SEA for low pressure, HSE for high pressure, MGC for low
gravity, BL for high gravity, INS for cold, TSH for heat - area-scaled
where applicable), confirmed against live in-game build panels on Nike.
FIO's own BuildRequirements field is an unreliable superset across hazard
types, not a direct per-building list - don't use it directly.

- puga/planet.py::environment_extras(): the real threshold table.
- tools/buildcost.py: full construction material list for one building,
  optionally on a given planet.
- Wired into whatif.py/buy.py (via simulate(planet=...)) and scan.py's
  own bcost(), so their capex numbers stop silently omitting this.
- docs/mechanics.md: the rule, plus a separate forum-sourced note on the
  CX order-cancellation penalty.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-10-17 19:03:51 +02:00
dodoxandClaude Sonnet 5 6bd5d6cc69 Add CONTD contract evaluator, deadline-aware buy pricing, actpkg JSON generator
- puga/contd.py, tools/contd.py: price open CONTD contract offers against real
  buy-fresh cost, flagging conditions that can't be priced from the API alone.
- tools/buyat.py: cheapest bid price likely to fill by a hard deadline
  (stockout risk), using the empirical first-passage model from history.py.
- puga/actpkg.py, tools/actpkg.py: Refined PrUn XIT ACT buy-list JSON packages.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-10-17 19:03:42 +02:00
dodoxandClaude Sonnet 5 dbe5cea9ae sell.py: fix off-by-one in queue-ahead depth
A resting order at the same price was posted first and has time
priority, so it should count as ahead too (p <= r["price"], not p <).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-10-17 19:03:35 +02:00
dodoxandClaude Sonnet 5 3ee26e5a85 Fix wording/tests: marginal_freight docstring, structural simulate() check
- marginal_freight()'s docstring claimed small additions "cost ~0" if
  they fit in slack - the code is actually a continuous amortized rate
  with no such threshold. Fixed the wording to match the real behavior.
- The one known failing test asserted fixed money values from a 2026-09-18
  screenshot against live, drifting market prices - removed that
  assertion (it was testing market drift, not code correctness) but kept
  the exact area check. Replaced with a structural, price-independent
  regression check instead of just deleting: degradation == prod_cost/180
  and plan_cost == prod_cost+infra_cost+cm_cost, asserted directly from
  simulate()'s own fields, holds at any price.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-10-10 23:06:55 +02:00
dodoxandClaude Sonnet 5 3f67cc7ed5 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>
2026-10-10 23:06:49 +02:00
dodoxandClaude Sonnet 5 11f54276b3 Add tools/parkcash.py: idle-cash park/flip scan using the empirical model
Where to park idle cash in a buy-low/sell-high position while it has no
better use - scans every ticker for real bid/ask spread and liquidity,
scoring round-trip (buy-dip, sell-recovery) via the same empirical
first-passage model as sell.py's optimal-ask fix, instead of an assumed
random walk. Dropped the universe's top pick from ~94%/day to ~1%/day
with real waits of 19-194+ days under the corrected model. Default
--rate 0.0 matches the tool's own idle-cash premise (sell.py's rate of
0.05 assumes cash is earmarked, which isn't this tool's case).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-10-10 23:06:40 +02:00
dodoxandClaude Sonnet 5 c4bcc8f42c sell.py: optimal-ask model via empirical first-passage, not random walk
A driftless-random-walk closed form was tried first and backtested wrong
against 3 years of real exchange history (overstated touch probability
2-4x, confounded by discrete daily-VWAP sampling and real mean-reversion).
Replaced with puga.history.empirical_touch(): a non-parametric estimator
measuring first-passage directly from a ticker's own history, conditioned
on current state (trailing-60d z-score) since mean reversion's effect
depends on where price is now, not just move size. Queue-depth (existing
resting quantity) folds in as a deterministic shift of the hitting time,
not a max, ranking by the resulting hit_factor rather than a point
estimate of median days (which created a false cliff at the 50%-touched
threshold). Optional fixed-horizon right-censoring avoids recency bias
in "never reached" samples.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-10-10 23:06:34 +02:00
dodoxandClaude Sonnet 5 74d05009fb Charge real freight in network.py/whatif.py; drop "ROI/day %" terminology
network.py: tools/network.py now computes freight cost from the real fuel
model (puga.shipping.round_trip_cost) for direct-jump transfers instead of
the flat 9250 AIC placeholder; gateway transfers or an explicit trip_cost
in the chain YAML still use that as a fallback. puga/network.py's combine()
docstring now flags that its "flows cancel, only freight matters" claim
only holds under a symmetric-price basis (uni30, the default) - under
--basis real it understates the benefit of physical transfer by the
bid-ask spread on the moved quantity.

whatif.py: now charges marginal freight too - only the EXTRA round trips
an addition needs beyond the base's existing traffic, so a small addition
riding along on already-necessary hauls costs ~0, while one that adds
meaningfully to shipped tonnage doesn't get a free pass.

Terminology: "profit/day ÷ capex" was being reported as "ROI/day X%",
which invites reading it as a compoundable rate. It's just 1/payback_days -
there's no reinvestment mechanism and every opportunity here is
capacity/market-limited, so it can't be annualized like a real yield.
Payback in days is now the primary number everywhere (scan.py, simulate.py,
network.py, whatif.py); see docs/decisions.md for the reasoning.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-10-09 13:33:13 +02:00
dodoxandClaude Sonnet 5 c660559f74 Add tools/lmscan.py: live Local Market hauling-ad scan
Pulls real player-posted freight jobs from FIO's /localmarket/shipping
endpoints (public, no auth - distinct from the CONTD player-contract
system, which isn't exposed via any API). Ranks by margin after real
one-way fuel cost (puga.shipping.leg_cost), flagging ads that exceed the
account's ship hold or route through an unmeasured gateway.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-10-09 13:33:04 +02:00
dodoxandClaude Sonnet 5 9a4c7cc57c Add real ship flight fuel/cost model (puga/shipping.py)
Calibrated from a controlled slider sweep across 5 real destinations, read
from the live APEX Ship Flight Control DOM. Covers STL cruise, FTL
jumps/charges, gateway hops (measured LOCK fees for ANT<->HRT/BEN), and
per-planet landing fuel. Scoped to this account's one hull/reactor; open
gaps (gravity dependence, hull generalization, gateway one-way links,
per-gateway volume caps) are documented in the module docstring.

tools/shiptrip.py is the simple entry point (ship + from + to, works out
jumps/charges/landing itself); tools/shipcost.py takes explicit physics
params for when the breakdown is already known. puga/route.py now also
resolves CX station codes (ANT, HRT, ...), not just planets/systems - this
was needed for the gateway-aware lookups to work at all.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-10-09 13:32:58 +02:00
dodoxandClaude Sonnet 5 88dae754ca whatif.py: model demolition correctly - reclaim materials aren't a dead loss
Was wrong earlier this session claiming demolition gives nothing back.
FIO's /rain/userplanetbuildingreclaimables confirms partial material
return (observed 50-90% depending on material, not a fixed fraction -
looked up live, not approximated). --remove now prices the real
reclaim for anything actually built and nets it against new capex,
so a demolish-and-reinvest question gets the true net cash picture
instead of double-penalizing the removed building.

Used this to check "demolish BHP's HWP, build a 2nd CHP or POL
instead" - both are clearly net losers even counting the ~57,750 AIC
reclaim credit (net capex still positive, and marginal profit is
negative either way) - dominated by just adding the new building
without removing anything, since nothing scarce actually forces the
swap.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-10-09 10:36:05 +02:00
dodoxandClaude Sonnet 5 59245ee54d whatif.py: add --remove for teardown/demolish scenarios
Mirrors --add - removes AMOUNT (default: all) of a building from the
copied spec. Used this to check whether BHP's HWP is still worth
running after its price drop (still +19.8k-28.4k/day marginal,
nowhere near a teardown call) without hand-writing two spec files.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-10-09 10:30:46 +02:00
dodoxandClaude Sonnet 5 c4178fe779 Add tools/undervalued.py: trading strategy, goods priced below production cost
Finds manufactured goods currently selling below their implied
production cost (live input cost vs output price) - candidates to buy
and hold for a price-reversion bounce, the mirror of the "is this
worth building" question saturation.py already answers.

Backtested first (326 output tickers, 287 deduplicated dip-onset
episodes across 112 distinct tickers): +3mo forward return mean
+21.3%/median +8.9%/73.2% hit rate at >=10 units/day volume - real
signal, not noise, but only ~0.10-0.24%/day, far below the 14-29%/day
marginal ROI this session's production builds found. Documented
prominently in the tool's own docstring and CLAUDE.md so it doesn't
get mistaken for a primary strategy - it's for excess idle capital
once better-yielding builds are exhausted, not before.

Promoted daily_prices() out of tools/history.py into puga/history.py
so both tools share it, per the self-improve rule.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-10-09 10:28:34 +02:00
dodoxandClaude Sonnet 5 80b8fa8bf9 Add tools/planet.py: rank planets for a resource with real founding cost
Ranks every planet with a given extraction factor using FIO's bulk
planet data, pricing founding cost straight from FIO's own
BuildRequirements (real environment-extras materials for that specific
planet, not guessed temperature/pressure thresholds - PRUNplanner's own
calculations don't model those either). Takes --from an existing base
to report jump distance via tools/route.py, since pure-factor ranking
alone is misleading: the best-factor planets found for FEO were all
500-2600+ ly away, confirming Vulcan (merely decent factor, same system
as Deimos) was still the right practical pick.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-10-08 10:42:23 +02:00
dodoxandClaude Sonnet 5 84a873f3bc tools/chain.py: add --consumers to list what eats a material
Reverse of the existing make-vs-buy tree: given a ticker, find every
recipe that consumes it as an input, with each output's live market
depth. Answers "is there a deeper downstream product worth chaining
into" without an ad-hoc fio.recipes() scan each time (did this by hand
for BOR, BSE and BBH this session).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-10-08 10:42:16 +02:00
dodoxandClaude Sonnet 5 264fbf164b Add tools/whatif.py: marginal ROI of adding to a base you already run
Formalizes a pattern repeated by hand all session: copy the current
whole-base plan, add a building/recipe, diff simulate() against the
baseline for the true marginal profit and capex (not PRUNplanner's
plan-level ROI, which divides whole-plan profit by only the new capex
and badly overstates once a base exists).

Also flags any workforce tier the addition newly overloads, distinct
from a pre-existing overload it didn't cause - this is what caught the
PP2/pioneer-housing and 2nd-CHP/pioneer-housing surprises by hand
before this tool existed.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-10-08 10:42:11 +02:00
dodoxandClaude Sonnet 5 2aeb0e5674 Add tools/route.py: jump-distance routing from FIO's system graph
PRUNplanner doesn't model travel, so nothing in the project could answer
"how far is planet X" until now. Builds a graph from /systemstars (698
systems, real jump connections + 3D coordinates) and runs Dijkstra
weighted by straight-line ly (what FTL fuel/time actually scale with),
reporting jump count alongside.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-10-08 10:42:03 +02:00
dodoxandClaude Sonnet 5 b24f132210 Add runway tool with auto-detected inbound ships; fix multi-base state tagging
- tools/runway.py: days of runway per material before a base stalls, from simulate()'s real
  net consumption, base storage, plus (by default) any ship currently flying to that planet
- tools/state.py: tag each STORE entry with its planet (FIO AddressableId -> site SiteId) and
  each ship with its live flight origin/destination (keyed by ShipId, not StlFuelStoreId).
  Needed now that a 2nd base exists - storage/ship data was previously unscoped, which would
  have silently mixed bases' inventories together
- tests, docs updated (docs/library.md notes the multi-base tagging as a reusable pattern)

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-28 21:56:18 +02:00
dodoxandClaude Sonnet 5 58db08b921 Add multi-base chain modelling, fix price-band-aware demand/supply, tranche-split sell tool
- puga/network.py + tools/network.py: model a multi-base chain (e.g. mine LST at one base,
  ship it, consume it at another). Simulates each base independently, nets a transferred
  material's producer-surplus against consumer-need, charges real freight only on what's
  moved, cm_free for a founding covered by a Core Module Kit. Worked example in plans/chains/.
- puga/saturation.py: real price-band filtering. FIO's order_book NarrowPriceBandLow/High and
  WidePriceBandLow/High match APEX's own displayed Price Band exactly (verified live) - an
  order outside it is a stale artifact, not just uncompetitive. Added in_band() and
  effective_demand(); effective_supply() gained the same hard band filter alongside its
  existing soft vwap-proximity filter (renamed that param mult to free up `band`). Wired into
  tools/scan.py stage 2 in place of the raw, unfiltered demand figure. Was flagged as an
  unimplemented refinement in saturation-design.md since the original design review.
- tools/sell.py: self-contained tranche split (aggressive tranche capped at a volume quantile,
  median normally or 80th pct with --tight when a payment is imminent and stockout risk
  outweighs margin; patient tranche priced under the next competitor tier). Instant-bid
  comparison moved behind --show-bid (off by default).
- Docs and roadmap updated accordingly.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-25 14:32:09 +02:00
dodoxandClaude Sonnet 5 e2e592cae3 Add buy/sell tools, library docs, and permit/HQ fixes
- tools/buy.py: cash-aware shopping list (construction gap vs empire state's built
  buildings, plus N days of NET operating stock via simulate() so self-produced
  inputs net against consumption), priced at real order-book fill cost, checked
  against cash/reserve, flags multi-trip, binary-searches an affordable size when short
- tools/sell.py: where to post an ask vs hitting the bids now, with expected
  clear time from 30-day traded-volume percentiles
- docs/library.md: module map and patterns for using puga.* directly instead of
  the CLI, plus the "simulate() only knows what the plan lists" gotcha that a
  first version of buy.py hit
- tools/simulate.py: fix --no-hq being a no-op; tools/state.py: permits_total_override
  for when the in-game HQ screen disagrees with FIO's MaximumPermits
- docs/mechanics.md: HQ display vs PRUNplanner's HQ flag, workforce arrival sources
- CLAUDE.md, docs/roadmap.md updated

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-22 17:40:17 +02:00
dodoxandClaude Sonnet 5 4c186ac677 Add the puga command and plan pull; document the UI plan workflow
- pyproject.toml + puga/cli.py: `pip install -e .` installs a `puga <tool>` command, so no
  venv path is needed (replaces requirements.txt)
- tools/plan_push.py: `pull <uuid>` reads a plan (incl. UI edits) back into a YAML spec;
  recipe entries may carry an amount so switched-off recipes round trip; fixes a variable
  that overwrote the plan name in build_payload, with a regression test
- README: usage with the puga command, and a section on working on the same plans in the
  PRUNplanner UI and from Claude (write, edit in UI, read back)
- CLAUDE.md updated accordingly

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-19 00:37:20 +02:00
dodoxandClaude Sonnet 5 3bbf524ebb Add simulator replica, persistence, planet scan, README; split empire state out of the repo
- tools/simulate.py + puga/simulate.py: replica of PRUNplanner's simulator (flows and
  efficiency verified against screenshots), reports real new capex (planned minus built)
- tools/scan.py: staffing variants, freight, HQ/experts, --planet mode, demolish-later,
  --min-n as a pure market-size filter, --json output
- tools/history.py, tools/persistence.py: margin history and short-horizon payback checks
- tools/plan_push.py: guarded delete; tools/state.py: syncs to empire/
- README with features and setup; CLAUDE.md made generic
- Own-empire material (profile, state, plans, notes) moved to gitignored empire/;
  generic examples in plans/examples and state/company.example.yaml

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-19 00:11:02 +02:00
dodoxandClaude Sonnet 5 7a538cb300 Initial PuGa toolkit: data layer, econ, depth-aware scan, state sync, plan push
- puga/: cached FIO + PRUNplanner clients, market view with order-book walk,
  econ formulas ported from PRUNplanner (tested against its suite and live FIO),
  saturation model v1 (reviewed by Opus)
- tools/: scan (depth-aware), price, book, chain, state sync, plan_push
  (dry run default, [PuGa]-prefixed plans only), legacy prun_scan/prun_cxarb
- docs/: mechanics (PRUNplanner is source of truth), roadmap, decisions,
  saturation design, archived handoff
- secrets stay in .env (gitignored); ref/ holds PRUNplanner source (ignored)

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-18 23:00:21 +02:00