codex: complete GE-flip lifecycle per owner addendum — partial fills, ge_cancel recovery, ledger, break/resume, realized rates (bounce-fix 4)

Owner lifecycle addendum (2026-08-28) + governor bounce 4 (lifecycle contract):
- state machine now covers the ENTIRE buy -> sell -> collect flow with no
  stranded state: partial fills tracked per slot (filled_qty ledger), 4h-lapse
  buys go to a new cancel_requote state (ge_cancel: units return to inventory,
  cash refunds, slot freed) instead of collect, cancelled units still sell at
  the high edge, lapse sells re-quote or lower rather than strand
- action grammar + schema extended ONCE with the cancellation mutation
  ge_cancel (was missing; ge_clear/ge_offers_done are predicates only) and
  used explicitly in the new a8 cancel-requote action
- concrete runtime state: minimum per-slot capital table (buy x 4h limit),
  members 8-slot vs F2P 3-slot paths, per-slot offer phase + quantities,
  buy-limit window, inventory/collection reconciliation in a9
- breaks connected to an explicit safe/resume path: resting offers persist
  server-side + per-slot ledger survive the pause; resume = re-open GE,
  re-check offers, continue current phase; never break mid-collect/mid-place
- rates qualified against the full lifecycle: 149,500 gp/h is named the 4h
  buy-limit CEILING; realized range = ceiling x fill_factor (~0.5-0.9) x
  duty_cycle (~0.90-0.95) ~ 96,300 gp/h midpoint; gp_per_hour_low 74,750
- pinned worked example: capital 22.5M -> 8 slot quotes -> arrowtips partial
  fill (4,000/10,000 tracked) -> iron bar full fill sell (90,000 net) ->
  cosmic no-fill ge_cancel + requote -> arrowtips partial matures at lapse
  (cancel, sell 4,000, 68,000 net) -> first break after 6-10 rotations ->
  resume -> stop conditions
- version 2 -> 3; evidence: validate 2 entries 0 errors 1 pre-existing WARN,
  selftest 4 PASS (fractional-tax fail, ge non-margin pass, floored pass,
  above-cap pass), synthetic merge main 79cab78 + head clean + gate green,
  git diff --check clean
This commit is contained in:
Claude 2026-08-28 03:19:13 -05:00
parent 00bfc358cb
commit 3572255a11
3 changed files with 182 additions and 50 deletions

View File

@ -3,17 +3,22 @@ title: GE Flipping (margin trading)
domain: economy domain: economy
tags: [flipping, merchanting, ge, money-making, hybrid] tags: [flipping, merchanting, ge, money-making, hybrid]
status: verified status: verified
version: 2 version: 3 # 3: bounce-fix 4 2026-08-28 (owner lifecycle addendum) — complete runnable flow: partial fills, ge_cancel + requote recovery, per-slot ledger, break/resume path, lifecycle-qualified rates, worked example pinned; ge_cancel added to the action grammar + schema
updated: "2026-08-27" # ISO dates must stay quoted — YAML 1.1 parses bare dates to datetime.date updated: "2026-08-28" # ISO dates must stay quoted — YAML 1.1 parses bare dates to datetime.date
summary: > summary: >
Buy-and-sell Grand Exchange margin flipping: place resting buy offers at Buy-and-sell Grand Exchange margin flipping: place resting buy offers at
the low edge of an item's price band and resting sell offers at the high the low edge of an item's price band and resting sell offers at the high
edge, harvesting the spread on high-volume items. There are no skill or edge, harvesting the spread on high-volume items. There are no skill or
quest requirements — capital breadth and slot discipline are the whole quest requirements — capital breadth and slot discipline are the whole
game. Members accounts run 8 GE slots over the full candidate set; game. Members accounts run 8 GE slots over the full candidate set; F2P
F2P accounts get 3 slots and F2P-tradeable rows only (separate F2P path accounts get 3 slots and F2P-tradeable rows only (separate F2P path in
in profit_drivers). profit_drivers). Lifecycle (owner addendum 2026-08-28): the entry models
the complete runnable flow — capital allocation, per-slot quotes with a
quoted/filled/sold/collected ledger, partial fills, ge_cancel +
requote recovery on window lapse or stale prices, collection and
reconciliation, breaks on resting offers, and explicit stop conditions —
with a pinned worked example and realized (not idealised) rates.
accounts: accounts:
membership: hybrid # members: 8 slots/full set; F2P: 3 slots/F2P rows (see economy) membership: hybrid # members: 8 slots/full set; F2P: 3 slots/F2P rows (see economy)
@ -22,6 +27,7 @@ accounts:
requires_other: requires_other:
- "Post-Tutorial Island account with Grand Exchange access (no skill or quest requirement)" - "Post-Tutorial Island account with Grand Exchange access (no skill or quest requirement)"
- "Members account: 8 GE slots, full candidate set (the modeled path below). F2P account: 3 GE slots and F2P-tradeable rows only — run the F2P path in profit_drivers." - "Members account: 8 GE slots, full candidate set (the modeled path below). F2P account: 3 GE slots and F2P-tradeable rows only — run the F2P path in profit_drivers."
- "Minimum starting capital: per slot = buy edge x 4h limit (arrowtips 2.23M, iron bar 1.45M, cosmic 2.196M, silver ore 0.949M, swordfish 3.09M, lobster 1.59M, magic logs 9.264M, mud 1.728M at the 2026-08-27 snapshot) — full 8-slot members coverage ~22.5M; the 3-slot F2P path needs ~5.5M (iron 1.45M + cosmic 2.196M + silver 0.949M). Capital below one slot's requirement takes that slot off the table."
best_practice: best_practice:
equipment: equipment:
@ -58,15 +64,22 @@ best_practice:
members set — a hybrid account must swap to the 3-slot F2P path (Iron bar / members set — a hybrid account must swap to the 3-slot F2P path (Iron bar /
Cosmic rune / Silver ore) instead of chasing members-only rows it cannot buy." Cosmic rune / Silver ore) instead of chasing members-only rows it cannot buy."
source: "OSRS wiki — Grand Exchange (Members get eight Grand Exchange slots ... free players are limited to three. Additionally, free players may only purchase free-to-play items, and not members' items)" source: "OSRS wiki — Grand Exchange (Members get eight Grand Exchange slots ... free players are limited to three. Additionally, free players may only purchase free-to-play items, and not members' items)"
- name: keep a per-slot offer ledger; cancel-and-requote on lapse, never strand units
why: "Every slot tracks quoted qty -> filled qty -> sold qty -> collected proceeds.
A partially-filled offer keeps resting with its acquired units attached; on the 4h
window lapse or a stale price the slot is ge_cancel'ed — acquired units return to
inventory, unspent cash refunds — then re-quoted at the fresh edge (buy) or exited
at the current high edge (sell). No items, coins, or GE slots are ever stranded."
source: "OSRS wiki — Grand Exchange (offer mechanics; 4h window); docs/action-grammar.md (ge_cancel, added 2026-08-28)"
economy: economy:
# Model (documented, reproducible from the snapshot): # Model (documented, reproducible from the snapshot):
# Members path (8 GE slots — the modeled economics below): # Members path (8 GE slots — the modeled economics below):
# per slot: buy `limit` units at the low edge, sell at the high edge. # per slot: buy `limit` units at the low edge, sell at the high edge.
# tax_pu = floor(0.02 * sell) # 2% convenience fee, rounded DOWN to # tax_pu = min(floor(0.02 * sell), 5_000_000) # 2% convenience fee,
# # whole coins per item; the 5M/item cap # # rounded DOWN to whole coins per item,
# # and the sub-50gp rounding exemption # # capped 5M/item (owner decision
# # are not hit at these prices # # 2026-08-28; cap never hit here)
# net_pu = sell - tax_pu - buy # integer per-item net (floored tax) # net_pu = sell - tax_pu - buy # integer per-item net (floored tax)
# 4h/slot = net_pu * limit # buy-limit window caps per-4h volume # 4h/slot = net_pu * limit # buy-limit window caps per-4h volume
# F2P path (3 GE slots, F2P-tradeable rows only): same arithmetic, top-3 # F2P path (3 GE slots, F2P-tradeable rows only): same arithmetic, top-3
@ -74,13 +87,25 @@ economy:
# Snapshots 2026-08-27: prices.runescape.wiki mapping + latest; limits and # Snapshots 2026-08-27: prices.runescape.wiki mapping + latest; limits and
# members flags cross-checked against the wiki item pages (all agree). Top 8 # members flags cross-checked against the wiki item pages (all agree). Top 8
# by 4h/slot below in profit_drivers. # by 4h/slot below in profit_drivers.
#
# Lifecycle qualification (owner addendum 2026-08-28): the 4h figures are
# an economic CEILING — ideal full fills of every slot with zero overhead.
# The realized operating range is:
# realized gp/h = ceiling x fill_factor x duty_cycle, where
# fill_factor ~0.5-0.9 (partial fills, cancels, requotes reduce the
# filled volume) and duty_cycle ~0.90-0.95 (scan + place + collect
# ~3 min per ~40-min rotation; breaks every 6-10 rotations).
# Example midpoint: 149,500 x 0.70 x 0.92 ≈ 96,300 gp/h.
# gp_per_hour_low (74,750) = 50% of the ceiling — a conservative realized
# lower bound; gp_per_hour_high (149,500) = the named ceiling, never
# presented as the achieved runtime rate.
item: Rune arrowtips # anchor: top candidate by modeled 4h/slot (members) item: Rune arrowtips # anchor: top candidate by modeled 4h/slot (members)
item_id: 44 item_id: 44
cost_per_cycle: 223 # buy @ low edge per unit (snapshot 2026-08-27) cost_per_cycle: 223 # buy @ low edge per unit (snapshot 2026-08-27)
income_per_cycle: 244 # sell @ high edge per unit, gross (snapshot 2026-08-27) income_per_cycle: 244 # sell @ high edge per unit, gross (snapshot 2026-08-27)
net_per_cycle: 17 # 244 - floor(244*0.02) - 223 = 244 - 4 - 223 (floored tax) net_per_cycle: 17 # 244 - min(floor(244*0.02), 5M) - 223 = 244 - 4 - 223 (floored, capped tax)
gp_per_hour_low: 74750 # 50% of the top-8 modeled upper bound — partial fills/drift gp_per_hour_low: 74750 # realized lower bound = 50% of the ceiling: partial fills, cancels/requotes, collection trips, breaks
gp_per_hour_high: 149500 # top-8 modeled upper bound: sum(4h/slot) / 4 (8 slots, full fills) gp_per_hour_high: 149500 # 4h buy-limit CEILING: sum(4h/slot) / 4 (8 members slots, ideal full fills, zero overhead) — qualified above
xp_per_hour: 0 # flipping awards no XP xp_per_hour: 0 # flipping awards no XP
supplies: supplies:
- item: Rune arrowtips # canonical wiki names per action-grammar conventions - item: Rune arrowtips # canonical wiki names per action-grammar conventions
@ -98,70 +123,95 @@ economy:
profit_drivers: profit_drivers:
- "Spread per unit x trade volume; volume is the safety factor (high-volume items fill faster and drift less)" - "Spread per unit x trade volume; volume is the safety factor (high-volume items fill faster and drift less)"
- "Slot breadth: cover as many net-positive candidates as capital allows (members 8 slots / F2P 3 slots)" - "Slot breadth: cover as many net-positive candidates as capital allows (members 8 slots / F2P 3 slots)"
- "Members path — snapshot 2026-08-27 top 8 by modeled 4h/slot (floored 2% tax): Rune arrowtips 170,000 (M, buy 223 / sell 244 / limit 10000); Iron bar 90,000 (F2P, 145/157/10000); Cosmic rune 72,000 (F2P, 122/128/18000); Magic logs 72,000 (M, 772/793/12000); Silver ore 65,000 (F2P, 73/79/13000); Mud rune 54,000 (M, 96/101/18000); Raw swordfish 45,000 (F2P, 206/213/15000); Raw lobster 30,000 (F2P, 106/110/15000) — total 598,000 per 4h = 149,500 gp/h on 8 slots" - "Members path — snapshot 2026-08-27 top 8 by modeled 4h/slot (floored+capped 2% tax): Rune arrowtips 170,000 (M, buy 223 / sell 244 / limit 10000); Iron bar 90,000 (F2P, 145/157/10000); Cosmic rune 72,000 (F2P, 122/128/18000); Magic logs 72,000 (M, 772/793/12000); Silver ore 65,000 (F2P, 73/79/13000); Mud rune 54,000 (M, 96/101/18000); Raw swordfish 45,000 (F2P, 206/213/15000); Raw lobster 30,000 (F2P, 106/110/15000) — total 598,000 per 4h = 149,500 gp/h CEILING on 8 slots"
- "F2P path — 3 slots, F2P-tradeable rows only: Iron bar 90,000 + Cosmic rune 72,000 + Silver ore 65,000 = 227,000 per 4h = 56,750 gp/h (alternates: Raw swordfish 45,000, Raw lobster 30,000)" - "F2P path — 3 slots, F2P-tradeable rows only: Iron bar 90,000 + Cosmic rune 72,000 + Silver ore 65,000 = 227,000 per 4h = 56,750 gp/h ceiling (alternates: Raw swordfish 45,000, Raw lobster 30,000)"
- "Post-2%-fee reality: 11 of 21 classic staples are net-positive at band edges for members; sub-2%-spread items (law/chaos/blood runes, dragon bones, adamantite/mithril bars) need sub-low buys or above-high sells" - "Post-2%-fee reality: 11 of 21 classic staples are net-positive at band edges for members; sub-2%-spread items (law/chaos/blood runes, dragon bones, adamantite/mithril bars) need sub-low buys or above-high sells"
- "Realized vs ceiling (work the full lifecycle): realized gp/h = ceiling x fill_factor (~0.5-0.9, partials/cancels/requotes) x duty_cycle (~0.90-0.95, scan/place/collect + breaks) — e.g. 149,500 x 0.70 x 0.92 ~ 96,300 gp/h midpoint; do not quote the ceiling as an achieved rate"
state_machine: state_machine:
initial: idle initial: idle
terminal: [complete, failed] terminal: [complete, failed]
states: states:
idle: idle:
goal: be logged in at a bank with a GE booth, capital >= floor goal: "be logged in at a bank with a GE booth, capital >= floor, per-slot offer ledger empty (quoted = filled = sold = collected = 0 for every slot)"
next: [scan, complete, failed] next: [scan, complete]
scan: scan:
goal: read the current high/low band for every candidate goal: "read the current high/low band for every candidate; compute net-positive edges with floored+capped tax"
next: [buy, idle] next: [buy, idle]
buy: buy:
goal: place one resting buy offer per slot at or below the low edge goal: "place one resting buy offer per free slot at or below the low edge; record quoted qty and remaining 4h limit per slot in the ledger"
next: [wait_buy_fill, collect, idle] next: [wait_buy_fill, idle]
wait_buy_fill: wait_buy_fill:
goal: hold BUY offers; re-quote any approaching the 4h window lapse goal: "hold BUY offers; track filled_qty per slot (partial fills included, units stay attached to the resting offer); on the 4h window lapse or a stale price, cancel the unfilled balance"
next: [sell, buy, collect] next: [sell, cancel_requote]
sell: sell:
goal: place one sell offer per filled slot at or above the high edge goal: "place one sell offer per slot holding units (its filled_qty) at or above the high edge"
next: [wait_sell_fill, collect] next: [wait_sell_fill, cancel_requote]
wait_sell_fill: wait_sell_fill:
goal: hold SELL offers; re-quote any approaching the 4h window lapse goal: "hold SELL offers; track sold qty per slot; on lapse or a stale price, cancel and re-quote — units are never left stranded"
next: [collect, sell, idle] next: [collect, cancel_requote]
cancel_requote:
goal: "ge_cancel the timed-out/stale offers of the current phase: acquired units return to inventory, unspent cash refunds, slot freed; re-quote at a fresh edge (buy when margin is still positive; sell to exit at the current high edge) or free the slot"
next: [wait_buy_fill, sell, wait_sell_fill]
collect: collect:
goal: collect proceeds, deposit coins, log the cycle goal: "collect proceeds, deposit coins, reconcile (inventory empty of flip items, all GE slots free, each slot's ledger closed with a logged RESULT)"
next: [scan, complete, idle] next: [scan, complete, idle]
complete: complete:
goal: session GP target reached goal: "session GP target reached"
failed: failed:
goal: hard failure — stop, log ERROR sentinel, alert goal: "hard failure — stop, log ERROR sentinel, alert"
transitions: transitions:
- from: idle - from: idle
when: "capital below floor OR session target reached" when: "GE access available AND capital >= floor AND session target not reached"
to: scan
- from: idle
when: "capital below floor OR session target already reached"
to: complete to: complete
- from: scan - from: scan
when: "no candidate net-positive at band edges on 3 consecutive scans" when: "at least one net-positive candidate at the scanned band edges (floored+capped tax)"
to: buy
- from: scan
when: "no net-positive candidate on 3 consecutive scans (prices stale — stand down)"
to: idle to: idle
- from: buy - from: buy
when: "all slots quoted" when: "all free slots quoted (quoted qty recorded per slot)"
to: wait_buy_fill to: wait_buy_fill
- from: buy
when: "capital exhausted before quoting (insufficient funds)"
to: idle
- from: wait_buy_fill - from: wait_buy_fill
when: "all pending buys filled" when: "a slot's buy offer is FULLY filled (filled_qty == quoted qty) — that slot's units move to sell"
to: sell to: sell
- from: wait_buy_fill - from: wait_buy_fill
when: "4h window lapse with unfilled BUY offers" when: "a slot's buy offer hits the 4h window lapse or a stale price with filled_qty < quoted qty — ge_cancel: acquired units return to inventory, unspent cash refunds, slot freed"
to: collect to: cancel_requote
- from: cancel_requote
when: "re-quoted buy offers placed at the fresh low edge (window renewed, margin still positive)"
to: wait_buy_fill
- from: cancel_requote
when: "buy-side margin is gone — free the slot on the buy side; any acquired units from the cancelled offer still sell at the current high edge (exit at cost or a small loss rather than strand inventory)"
to: sell
- from: cancel_requote
when: "re-quoted sell offers placed at the fresh high edge (or lowered to the current band) for units in hand"
to: wait_sell_fill
- from: sell - from: sell
when: "sells placed" when: "sell offers placed for all units in hand (filled_qty per slot)"
to: wait_sell_fill to: wait_sell_fill
- from: wait_sell_fill - from: wait_sell_fill
when: "all pending sells filled" when: "all pending sells filled — per-slot proceeds ready to collect"
to: collect to: collect
- from: wait_sell_fill - from: wait_sell_fill
when: "4h window lapse with unfilled SELL offers" when: "a sell offer hits the 4h window lapse or a stale price — ge_cancel and re-quote at the fresh high edge or lower to the current band; units are never stranded"
to: sell to: cancel_requote
- from: collect - from: collect
when: "session GP target reached" when: "session GP target reached"
to: complete to: complete
- from: collect - from: collect
when: "rotation done and window open" when: "rotation done and window open — start the next rotation"
to: scan to: scan
- from: collect
when: "capital fell below floor after reconciliation"
to: idle
actions: actions:
- id: a1 - id: a1
@ -192,6 +242,7 @@ actions:
params: params:
items: "top-N candidates from the entry table" items: "top-N candidates from the entry table"
band: "high and low edge per item" band: "high and low edge per item"
margin: "net-positive = sell - min(floor(0.02 x sell), 5,000,000) - buy > 0"
on_fail: skip on_fail: skip
anti_ban: anti_ban:
delay: [1200, 2800] delay: [1200, 2800]
@ -202,9 +253,10 @@ actions:
target: grand-exchange target: grand-exchange
params: params:
item: "per candidate slot" item: "per candidate slot"
qty: "min(remaining 4h limit, slot capacity)" qty: "min(remaining 4h limit, slot capacity, capital available)"
price: "low edge (rounded down)" price: "low edge (rounded down)"
max_price: "low edge — never instant" max_price: "low edge — never instant"
ledger: "record quoted qty and remaining 4h limit per slot"
on_fail: skip on_fail: skip
anti_ban: anti_ban:
delay: [900, 2700] delay: [900, 2700]
@ -214,39 +266,71 @@ actions:
verb: sleep verb: sleep
params: params:
delay: [6000, 12000] delay: [6000, 12000]
until: "ge_offers_done changes or 4h window lapse" until: "ge_offers_done changes or a fill event or 4h window lapse"
on_fail: retry on_fail: retry
anti_ban: anti_ban:
delay: [300, 900] delay: [300, 900]
- id: a6 - id: a6
step: check-fills
verb: ge_offers_done
target: "all quoted slots"
params:
phase: "buy (no pending sells) | sell (no pending buys)"
per_slot: "filled_qty vs quoted_qty for every slot (partial fills included)"
verdict: "full | partial | none | 4h-lapse"
on_fail: retry
- id: a7
step: place-sells step: place-sells
verb: ge_sell verb: ge_sell
target: grand-exchange target: grand-exchange
params: params:
item: "per filled slot" item: "per slot holding units (filled_qty)"
qty: "filled quantity" qty: "filled_qty of that slot"
price: "high edge (rounded up)" price: "high edge (rounded up)"
ledger: "record sold qty per slot"
on_fail: skip on_fail: skip
anti_ban: anti_ban:
delay: [900, 2700] delay: [900, 2700]
- id: a7 - id: a8
step: cancel-requote
verb: ge_cancel
target: "timed-out or stale slot offers (current phase)"
params:
offers: "the slots that hit the 4h window lapse or a stale price"
cancelled: "acquired units return to inventory; unspent cash refunds; slot freed"
requote: "buy: re-place at the fresh low edge when the margin is still positive; sell: re-place at the fresh high edge or lower to the current band; margin gone on the buy side: free the slot and sell any acquired units to exit"
on_fail: retry
- id: a9
step: collect-and-log step: collect-and-log
verb: ge_collect verb: ge_collect
target: grand-exchange target: grand-exchange
params: params:
deposit: "coins to bank" deposit: "coins to bank"
log: "RESULT item, qty, net gp per slot" reconcile: "inventory empty of flip items, all GE slots free, per-slot ledger closed"
log: "RESULT item, qty, net gp per slot (floored+capped tax)"
on_fail: alert on_fail: alert
anti_ban: anti_ban:
delay: [600, 1400] delay: [600, 1400]
- id: a10
step: take-break
verb: take_break
target: "current location"
params:
duration: "[60, 240] seconds of idle AFK per break (matches run_control.break.for)"
safe: "resting offers persist server-side at their quoted edges — the full state lives in the GE interface plus the per-slot ledger, so a break at any resting-offer point is safe"
resume: "re-open GE (a2), re-check offer state (a6), continue from the current phase (a4/a7/a8/a9) — never break mid-collect (coins in hand) or mid-place (offer half-configured)"
on_fail: skip
run_control: run_control:
iteration: "one full buy -> buy-fill -> sell -> sell-fill -> collect rotation across all slots" iteration: "one full rotation: quote buys -> fills/cancels/requotes -> sell filled units -> sells fill/cancel/requote -> collect + reconcile, across all slots"
max_iterations: null max_iterations: null
break: break:
every: [6, 10] # rotations every: [6, 10] # rotations (resting offers persist; state = GE + per-slot ledger)
for: [60, 240] # seconds of idle AFK for: [60, 240] # seconds of idle AFK
stop_conditions: stop_conditions:
- "session GP target reached" - "session GP target reached"
@ -254,12 +338,58 @@ run_control:
- "no net-positive candidates on 3 consecutive scans" - "no net-positive candidates on 3 consecutive scans"
- "3 consecutive aborted offers (client issue — do not loop)" - "3 consecutive aborted offers (client issue — do not loop)"
# ================= WORKED EXAMPLE (pinned, deterministic — owner addendum 2026-08-28) =================
# Members account, 8 GE slots, capital 22,500,000 gp. Snapshot prices 2026-08-27
# (buy/sell = low/high edge; tax = min(floor(0.02 x sell), 5,000,000)).
#
# Step 0 — preparation and capital allocation: capital at the bank, GE-adjacent.
# Scan bands (a3): only net-positive slots are quoted. Per-slot capital needs:
# arrowtips 223x10,000 = 2.23M; iron bar 145x10,000 = 1.45M; cosmic 122x18,000 =
# 2.196M; silver ore 73x13,000 = 0.949M; swordfish 206x15,000 = 3.09M; lobster
# 106x15,000 = 1.59M; magic logs 772x12,000 = 9.264M; mud 96x18,000 = 1.728M
# (total 22.497M <= 22.5M). Ledger: quoted=filled=sold=collected=0 per slot.
# Step 1 — place buys (a4): quote all 8 slots at the low edges (qty = 4h limit).
# wait_buy_fill (a5/a6).
# Step 2 — PARTIAL FILL: arrowtips fills 4,000 of 10,000 after ~18 min -> check-fills
# (a6) verdict PARTIAL: ledger arrowtips.filled = 4,000. The offer KEEPS resting
# (acquired units stay attached to the resting offer; nothing is stranded).
# Step 3 — FULL FILL: iron bar fills all 10,000 -> transition to sell (a7): place
# sell 10,000 @ 157 (high edge). wait_sell_fill -> fills -> collect (a9):
# proceeds 1,570,000; tax floor(157 x 0.02) = 3/item -> net = (157-3-145) x 10,000
# = 90,000. Reconcile: inventory empty, slot free, ledger closed.
# Step 4 — NO-FILL / STALE recovery on the buy side: cosmic 18,000 @ 122 is unfilled
# approaching the 4h lapse -> cancel_requote (a8, ge_cancel): 0 units return,
# 2,196,000 cash refunds, slot freed; re-scan (a3): cosmic still net-positive ->
# requote 18,000 @ 122 on the fresh window. (If the buy margin had vanished the
# slot is freed on the buy side; any units acquired from a cancelled offer still
# sell at the current high edge.)
# Step 5 — PARTIAL matures at lapse: arrowtips still 4,000/10,000 -> ge_cancel:
# 4,000 units return to inventory, 6,000 x 223 = 1,338,000 refunded -> sell (a7):
# 4,000 @ 244 -> tax floor(244 x 0.02) = 4/item -> net = (244-4-223) x 4,000 =
# 68,000. Collect (a9) + reconcile.
# Step 6 — FIRST SCHEDULED BREAK after 6-10 rotations (midpoint 8): execution is at
# a safe point — every slot is either collected or resting at its quoted edge; the
# state (offers + per-slot ledger) persists server-side. take-break 60-240 s
# (a10); RESUME at a2 (re-open GE) -> a6 (check-fills) -> continue the current
# phase (a4 buys / a7 sells / a8 cancel-requote / a9 collect). Breaks never strand
# an offer and never interrupt a collect/place in progress.
# Step 7 — STOP: session target (e.g. 900,000 net) -> collect -> complete; capital
# fell below floor -> idle -> complete; 3 consecutive aborted offers -> failed.
# Rate qualification: the 149,500 gp/h figure is the 4h buy-limit CEILING (8 slots,
# full fills, zero overhead). Realized midpoint ~ 149,500 x 0.70 (fill factor) x
# 0.92 (duty cycle incl. scan/place/collect + breaks) ~ 96,300 gp/h — the entry's
# low bound 74,750 (50% ceiling) sits under that; the ceiling is never presented
# as an achieved runtime rate.
# =====================================================================================================
risk: risk:
ban_level: low ban_level: low
notes: notes:
- "Flipping is standard gameplay; the detectable signal is quote churn — keep per-offer pacing humanised, never instant-buy/sell loops." - "Flipping is standard gameplay; the detectable signal is quote churn — keep per-offer pacing humanised, never instant-buy/sell loops."
- "The 2% fee (since 2025-05-29) and 5M/item cap make sub-2%-spread staples unprofitable at band edges; re-scan before every rotation." - "The 2% fee (since 2025-05-29) and 5M/item cap make sub-2%-spread staples unprofitable at band edges; re-scan before every rotation."
- "Thin/volatile items: a resting offer can sit for hours — cap quote lifetime to the 4h window and re-quote rather than chase." - "Thin/volatile items: a resting offer can sit for hours — cap quote lifetime to the 4h window and re-quote (ge_cancel + fresh edge) rather than chase."
- "Partial fills are normal; never cancel-and-replace a partially-filled offer mid-window just to churn — the ledger keeps the filled qty attached to the resting offer until full/lapse."
- "Breaks are safe only at resting-offer or post-collect points (see a10); state = GE offers + the per-slot ledger, which survive the pause."
sources: sources:
- "OSRS wiki — Grand Exchange (2% convenience fee since 2025-05-29, capped 5M/item, sub-50gp effectively untaxed, tax-exempt low-value items; buying rate restricted per 4h; connected limits; verified 2026-08-27)" - "OSRS wiki — Grand Exchange (2% convenience fee since 2025-05-29, capped 5M/item, sub-50gp effectively untaxed, tax-exempt low-value items; buying rate restricted per 4h; connected limits; verified 2026-08-27)"
@ -267,7 +397,8 @@ sources:
- "OSRS wiki — Merchanting (flipping = spread exploitation; high trade volume = safety; bulk flipping needs ~20M+ cash; avoid static-price items; offer matching at or better than your price)" - "OSRS wiki — Merchanting (flipping = spread exploitation; high trade volume = safety; bulk flipping needs ~20M+ cash; avoid static-price items; offer matching at or better than your price)"
- "prices.runescape.wiki/api/v1/osrs/mapping (item ids, members flags, limits — limits cross-checked against Module:Exchange/<Item>, all agree)" - "prices.runescape.wiki/api/v1/osrs/mapping (item ids, members flags, limits — limits cross-checked against Module:Exchange/<Item>, all agree)"
- "prices.runescape.wiki/api/v1/osrs/latest (high/low price band snapshot 2026-08-27)" - "prices.runescape.wiki/api/v1/osrs/latest (high/low price band snapshot 2026-08-27)"
verified: "2026-08-27" # keep quoted (YAML 1.1 date parsing) - "docs/action-grammar.md (ge_cancel verb added 2026-08-28: cancel a pending offer — bought units return to inventory, unspent cash refunds) + schemas/entry.schema.json verb enum; owner lifecycle addendum 2026-08-28 (complete runnable flow, worked example, realized rates)"
verified: "2026-08-27" # keep quoted (YAML 1.1 date parsing); prices last re-fetched live 2026-08-27
related: related:
- low-alch-f2p - low-alch-f2p

View File

@ -68,6 +68,7 @@ Legend for the mapping table:
| `ge_open` | open GE at a bank | — | — | — | | `ge_open` | open GE at a bank | — | — | — |
| `ge_buy` | buy `qty` of `item` at offer price, paced | — | — | — | | `ge_buy` | buy `qty` of `item` at offer price, paced | — | — | — |
| `ge_sell` | sell `qty` of `item`, confirm | — | — | — | | `ge_sell` | sell `qty` of `item`, confirm | — | — | — |
| `ge_cancel` | cancel a pending offer: bought units return to inventory, unspent cash refunds | — | — | — |
| `ge_offers_done` | predicate: no pending buy/sell offers | — | — | — | | `ge_offers_done` | predicate: no pending buy/sell offers | — | — | — |
| `ge_collect` | collect finished GE transactions | — | — | — | | `ge_collect` | collect finished GE transactions | — | — | — |
| `ge_check_price` | read current buy/sell price of an item | — | — | — | | `ge_check_price` | read current buy/sell price of an item | — | — | — |

View File

@ -155,7 +155,7 @@
"click_button", "menu_option", "click_button", "menu_option",
"bank_inventory", "deposit_all", "withdraw", "has_item", "bank_inventory", "deposit_all", "withdraw", "has_item",
"count_item", "move_to_inv", "sort_inventory", "count_item", "move_to_inv", "sort_inventory",
"ge_open", "ge_buy", "ge_sell", "ge_offers_done", "ge_collect", "ge_open", "ge_buy", "ge_sell", "ge_cancel", "ge_offers_done", "ge_collect",
"ge_check_price", "ge_check_price",
"attack", "prayer", "buff", "eat", "retreat", "is_dead", "respawn", "attack", "prayer", "buff", "eat", "retreat", "is_dead", "respawn",
"is_at", "item_count", "level_is", "xp_is", "hp_is", "is_at", "item_count", "level_is", "xp_is", "hp_is",