PAYMENT/REPUTATION always go to the player; PROVISION_SHIPMENT (a cost) and
PICKUP/DELIVERY_SHIPMENT (a freight leg the player may or may not fly) depend
on the condition's own Party field vs the contract's Party, not a flat
assumption that every material condition is something the player buys. Net
now correctly adds COMEX_PURCHASE_PICKUP rewards instead of only subtracting
provisioning costs.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
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>
- 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>
- 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>
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>
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>
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>
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>
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>
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>
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>
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>
- 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>
- 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>
- 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>