What This Tool Ranks
The calculator ranks NPC LP-store offers by realistic ISK per LP. It compares selling into Jita buy orders with listing against Jita sell orders, after taxes, broker fees, required item costs, and blueprint manufacturing material costs when that mode is enabled.
The default leaderboard:
- Shows all access-risk tiers and ranks by the higher of buy-order and sell-order ISK/LP.
- Groups duplicate store economics, uses
minVolume=0, and assumes30000LP/hour for the ISK/hour column. - Excludes SDE corporations without an earnable LP source before any user filter runs; normal agent, CONCORD/special, and faction warfare LP sources remain eligible. Level 5 mission corporations are shown by default.
- Enables the Warns, Vanity, and Sec L4+ quality filters, so rows with strong warnings or multiple warning flags, vanity/cosmetic products, plus corporations without normal level 4 or 5 Security agents are hidden unless explicitly enabled.
- Includes standard corporation LP; faction warfare, special-source LP, and blueprint-copy rows (both direct sale and manufacture) are opt-in.
- Keeps rows below
100units and250mISK of average daily market volume visible but flags themLOW_VOLUME. Rows above100m3 carry a cargo-volume flag, with a stronger red flag above500m3.
The Space control can narrow results to highsec-accessible stores or show all known access tiers. The Level 5 Missions control can show all corporations, only corporations with level 5 agents, or hide them. Ranking controls can choose the valuation basis, scale runs, set LP/hour, require minimum 28-day market volume, and optionally hide rows above a maximum packaged cargo volume in m3. Quality toggles control Jita-only pricing, suspicious rows, vanity rows, no-Security-L4+ corporations, faction warfare, special LP stores, and duplicate store rows. The BPC control picks which blueprint-copy rows appear: none, direct contract sales, manufacture conversions, or both.
Data Sources
- LP offers: public ESI
/latest/loyalty/stores/{corporation_id}/offers/. - Jita orders: public ESI region 10000002 market orders, persisted as top 50 sell and buy levels per type.
- Market history: public ESI region 10000002 history, summarized into 28-day average volume and median price.
- Static data: official Fenris Creations (formerly CCP Games) JSON Lines SDE ZIP, cached under
data/sde/and imported into SQLite. - Earnable LP sources: derived from SDE agent references for normal, CONCORD/special, and faction warfare agent corporations, then applied before LP-store fetches and leaderboard filters.
- SDE metadata: recorded in
source_importsand exposed through/api/health.
Refresh Cadence
The scheduler refreshes:
- Hot market prices every 15 minutes.
- Cold referenced market types hourly at minute 7.
- LP offers daily at 11:10 UTC, market history daily at 11:20 UTC, and snapshots daily at 11:45 UTC.
Startup warmup fills missing or stale SDE, LP, market, and history data before normal cadence takes over. Fetchers honor ESI Expires headers through the SQLite ESI cache, retry transient upstream failures carefully, and pause before exhausting the ESI error-limit budget.
The leaderboard header reports hot and cold market-price ages separately, LP offer freshness as a daily feed, and an overall Updates OK or Needs attention state from /api/health. A daily LP timestamp that is a few hours old is expected; hot prices are the freshness signal that should normally move every 15 minutes.
Core Calculation
buy_order_sell = sell products into buy orders, pay sales tax
sell_order_listing = list products near sell orders, pay sales tax plus one broker-fee pass
input_cost = LP-store ISK cost + required items bought from sell orders
build_cost = BPC materials bought from sell orders
net_buy_order = product_value_buy_order * (1 - sales_tax_rate) - input_cost - build_cost
net_sell_order = product_value_sell_order * (1 - sales_tax_rate - broker_fee_rate) - input_cost - build_cost
isk_per_lp = selected net_profit / LP cost
The valuation basis can use buy-order cashout, sell-order listing, or the higher of both. runs scales LP, ISK, required items, manufacturing materials, cargo, and order-book depth. When lpBudget or iskBudget is provided, the calculator chooses the maximum feasible run count for that row. The ISK/hour display multiplies the selected ISK/LP basis by the URL-backed lpPerHour value. Contract-priced rows show a dash instead: their ISK/LP is a real one-off conversion rate, but public contracts for such items sell at most a few times per day game-wide, so extrapolating by LP/hour would invent income the market cannot absorb. These rows also sort last when the leaderboard is sorted by ISK/hour.
Fees And Skills
sales_tax_rate = 0.075 * (1 - 0.11 * Accounting)
broker_fee_rate = max(
0.03
- 0.003 * BrokerRelations
- 0.0003 * factionStanding
- 0.0002 * corporationStanding,
0.01
)
These are the Tranquility NPC-station rates: sales tax starts at 7.5% and falls to 3.375% at Accounting V; the broker fee starts at 3%, drops 0.3 percentage points per Broker Relations level plus up to 0.3 from faction and 0.2 from corporation standing, and never goes below 1%. Accounting, Broker Relations, Advanced Broker Relations, faction standing, and corporation standing are URL-backed filters. Defaults are zero so the default view is pessimistic for a fresh character. Advanced Broker Relations only affects relist fees in Realistic patient mode.
Depth Walking
Every priced leg walks the persisted Jita order book for the full requested quantity instead of using only top-of-book. Products sold through buy orders walk buy orders. Products listed through sell orders, required items, and build materials walk sell orders. If the saved top 50 orders do not cover the requested fill, the row is flagged as insufficient depth and the missing quantity is extrapolated at the last consumed price.
Patient Listing Realism
The sell-order (patient) basis above assumes a listing fills in full at the walked prices. In reality a listing joins a queue and waits. Every row therefore carries an estimated fill time for the primary output, shown in the detail drawer:
queue_ahead = sell-book units listed at or below the volume-weighted list price
sell_rate = 0.5 * avg_daily_volume_28d
days_to_fill = (queue_ahead + quantity) / sell_rate
The 0.5 factor exists because ESI daily volume mixes both trade directions and only buyer-initiated trades consume the sell book; with no public split, half is the symmetric prior. Items with no market history get no estimate. The SLOW_FILL flag uses this queue-aware estimate for the output leg: warn past 7 days, strong past 28.
The optional Realistic patient quality toggle (off by default, URL-backed) goes further and discounts the sell-order valuation itself:
relist_discount = 0.5 + 0.06 * AdvancedBrokerRelations (80% at V)
expected_relists = ceil(days_to_fill / 2) - 1, floored at 0
relist_cost = expected_relists * (1 - relist_discount) * broker_fee_rate * product_value
patient_effective = net_buy_order + (net_sell_order - relist_cost - net_buy_order)
* exp(-days_to_fill / 7)
The exponential decay pulls slow fills toward the guaranteed buy-order cashout: a fill estimated a week out keeps about 37% of the patient premium, and a month-long fill keeps almost none. Relist fees model staying competitive against undercutting, using the Tranquility relist mechanic where modifying an order down costs the undiscounted share of the broker fee on the new price; Advanced Broker Relations is a URL-backed skill filter next to the other fee skills. With the toggle off, nothing changes — the patient and best columns keep the optimistic immediate-fill values.
BPC Manufacturing
When an offered product is a blueprint copy with a manufacturing recipe in the SDE, the calculator can value the manufactured output and subtract material costs ("manufacture" rows), and when fresh public-contract asks exist it can value the copy as a direct contract sale ("sell" rows). Both are hidden by default because blueprint rows can dominate the table with assumptions that are more sensitive to build settings, material liquidity, and contract depth. The four-position BPC control in the Quality filters picks which rows appear: None (default), Sell, Build (manufacture conversions), or All.
Risk And Access
Space filtering and row risk icons use practical LP-store access risk, derived from the least risky known NPC station owned by the corporation. The corporation HQ system is still shown as location metadata, but it does not by itself make a highsec-accessible corporation look like a nullsec-only store. Highsec is security 0.5 and above, lowsec is 0.0 through 0.4, and nullsec covers below 0.0 plus any wormhole or unknown-security store access.
DED, CONCORD, and similar special-source LP stores are marked as special LP and excluded from the global default leaderboard. Faction warfare offers are also opt-in. LP-store corporations with no earnable LP source, such as event or placeholder LP stores without agents, are excluded before these user-facing filters are applied. Risk chips are visual warnings on the row; highsec-accessible rows do not get a risk chip.
Quality Flags
| Flag | Rule | Severity |
|---|---|---|
LOW_VOLUME | 28-day average daily volume is below 100 units and below 250m ISK; expensive items that move serious ISK on few units are not flagged. | Warn; shown as a yellow flag. |
SLOW_FILL | The estimated sell-order fill of the output — queue ahead at the walked list price plus the requested quantity, against the sell-side half of daily volume (see Patient Listing Realism) — exceeds 7 days. Cost legs keep the plain quantity-versus-volume rule. | Warn; strong above 28 days. |
HEAVY | Packaged cargo volume is above 100 m3 and at or below 500 m3. | Warn; shown as a yellow flag. |
VERY_HEAVY | Packaged cargo volume is above 500 m3. | Strong visual; shown as a red flag. |
THIN_BOOK | Sell order count is 2 or less, or the top sell order has more than 80% of sell quantity. | Warn |
WIDE_SPREAD | Sell minimum is more than 50% above buy maximum. | Warn |
PRICE_SPIKE | Current sell minimum is more than twice the 28-day median price. | Strong |
BUY_SPIKE | Current buy maximum on the output is more than 1.5x the 28-day median price. Buy maxima normally sit below the median, so this usually means a seeded buy wall is inflating the buy-order valuation. | Strong |
OFF_HUB | Cheapest sell order is not at Jita 4-4. | Warn, or strong when multiple significant cost legs are off-hub. |
NO_HISTORY | Market history has fewer than seven days. | Warn |
INSUFFICIENT_DEPTH | Persisted order-book depth does not cover the requested fill. | Strong |
CONTRACT_PRICED | The product cannot be listed on the regular market (no market group, typically a faction blueprint copy) and is valued from scam-filtered public-contract asks in The Forge: blueprint copies are normalized to price per run, decoy listings are screened by a recipe cap (a copy's per-run value cannot exceed what the built product sells for) and a band anchored on the cheapest credible ask, and at least two independent asks must survive. The patient basis skips the sales tax and broker fee because item-exchange contracts pay only a flat creation fee; no instant-sell value exists. Blueprint offers with both a manufacture recipe and a contract price appear twice — a (sell) row for the direct contract sale and a (manufacture) conversion row whose net profit keeps total-realization math while the consumed copy's contract value counts into capital required and ROI; both are hidden until the BPC control enables them. | Warn |
NICHE_DEMAND | A contract-priced blueprint copy where one copy covers at least half a day of the built product's entire Jita demand (copy runs against the product's 28-day average daily volume). The whole market absorbs only a handful of fresh copies per day, so sale velocity — not LP/hour — caps what the offer can actually earn; the flag message states the copy-to-demand ratio. | Warn |
VANITY | The product is a vanity or cosmetic item: SDE apparel, SKIN, or personalization categories and groups, or a SKIN-named type. | Warn; frontend chip from row metadata. |
RISK_LOWSEC | Best known LP-store access is a low security station (see Risk And Access); highsec-accessible rows get no chip. | Warn; frontend chip from row metadata. |
RISK_NULLSEC | Best known LP-store access is null security or wormhole space. | Strong; frontend chip from row metadata. |
RISK_UNKNOWN | No known NPC station for the corporation, so the access risk tier is unknown. | Strong; frontend chip from row metadata. |
Tapping or clicking a flag chip on the leaderboard expands it into its full message; the ? inside links back to this section. Cargo-volume, vanity, and access-risk chips are added by the frontend from row metadata so they remain visible even when the persisted market-quality flags are otherwise clean.
Three quality filters interact with these flags:
- Hide suspicious removes any row with at least one strong persisted market-quality flag or at least two persisted market-quality warning flags.
- Jita-priced only requires every output, required input, and build-material sell leg to have its cheapest order at Jita 4-4.
- Sec L4+ is a separate optional quality filter that removes corporations with no normal level 4 or 5 Security agents in the SDE-derived agent reference; it does not control the base earnable-LP gate.
Search And Grouping
The compute step materializes offer names, product signatures, risk/source fields, flag counts, ratios, and compact API summaries into calc, then rebuilds SQLite FTS search for offer and product names. Corporation filtering uses a name autocomplete backed by corporation IDs. Identical offer economics are grouped by default so duplicate corporation stores do not crowd out distinct opportunities; "Show duplicate stores" returns each store row.
Frontend Loading And Diagnostics
The LP page starts the rows, corporations, and health requests without blocking the browser page-load event. It renders skeleton rows for a first load, reuses session-cached rows immediately for repeated filters, ignores stale filter responses, and shows a compact topbar loading state only while the current request is still active.
A persistent browser client ID is shown in the header and sent as X-EVE-Client-Id on API calls. The server also returns a fresh X-Request-Id for every request and writes problem-only JSON lines for 4xx and 5xx responses to logs/http-problems.log. The visible client ID helps correlate a player report without exposing request IDs in the UI.
API And Health
Read endpoints are rate-limited to 180 requests per minute per IP and set permissive CORS. Public LP API paths work at both /api/* and /lp/api/*. /api/health reports fetcher freshness, empty core tables, database size, last compute time, and SDE import metadata. POST /api/refresh recomputes the leaderboard and requires X-Admin-Token.
After each compute, canonical leaderboard, CSV, and corporation responses are materialized into SQLite with ETags and Brotli bodies. Dynamic read responses use short browser caching and longer CDN caching, while health stays short-lived. When Cloudflare credentials are configured, compute purges the LP API prefixes; deploys that change unhashed LP HTML/CSS/JS should run the static purge helper after build.
Known Limitations
- Market pricing is Jita-only.
- No ESI OAuth or per-character standing import exists in v1.
- Manufacturing uses the imported SDE recipe without ME/TE optimization or T2 reaction-chain modeling.
- Market history is summarized for liquidity and spike checks, not shown as long-range charts.
- The fill estimator assumes half of daily volume consumes the sell book and a fixed two-day relist cadence; ESI does not publish trade direction or order ages, so both are priors rather than measurements.
- The visible client ID is a support diagnostic only, not an ESI identity or account link.
- The missions section at
/missions/is a separate static reference surface and does not change LP calculations. - The current LP frontend is served directly at the
/lpbase path.