SlopScore
10 crowdincl. 2 critics

The-Ledger

Vibe coded tool for OSRS market analysis
Open repo on GitHubgithub.com/bensumm/The-Ledger
JavaScript · ★ 1 · 0 forks · MIT · paperwork by the Cap'mmostly ai (inferred)light human (inferred)works-on-my-machine (inferred)other
listed 1 hour ago by bensumm · last checked 1 hour ago
The owner didn't write this. This repo never submitted itself. The Cap'm found it on a truffle trawl and wrote its paperwork from what GitHub already shows. Picked by hand by the Cap'm on 2026-09-16: Vibe coded tool for OSRS market analysis; its own README says "Vibe coded tool for OSRS market analysis". 1 stars; MIT license. The owner did not submit this. Votes count; awards don't until the owner claims it.

I'm not calling your project slop! Geeze, it's a joke... Do you own this repo?

Log in with GitHub as bensumm. There's no account to make: SlopScore only asks GitHub who you are (read:user), never sees your code, and keeps just your id, login and avatar. Then you can:

  • Keep it, on your terms. Commit your own slopscore.md (spec) and press Refresh. Your paperwork replaces the Cap'm's, and you can submit it for Slop of the Day.
  • Take it down. One click on Remove. It stays gone; the trawl never brings it back.

Log in with GitHub

Can't log in as the owner? Request a takedown. No login needed, and a trawled listing comes down right away.

GitHub says
Vibe coded tool for OSRS market analysis
created
2026-06-21 · pushed 6 days ago · 987 commits · 1 contributor
languages
JavaScript 99%CSS 1%HTML 0%Batchfile 0%
paperwork
licensereadme 42% health
dependencies
no dependency graph (no manifest, or disabled) · OSV.dev, checked 1 hour ago

Disclosures, inferred by the Cap'm

slopbucket
vibe-coded
category
other
ai_generated
mostly
human_touch
light
status
works-on-my-machine
language (detected)
batchfilecsshtmljavascript
license (detected)
mit

The Cap'm's log

The Cap'm wrote this paperwork, not the owner. This repo never submitted itself to SlopScore. The Cap'm picked it by hand: Vibe coded tool for OSRS market analysis; its own README says "Vibe coded tool for OSRS market analysis". It carries the MIT license. The disclosures above are his best guess from what GitHub shows.

Is this yours? Commit a real slopscore.md and press Refresh to replace this, or remove the listing in one click. There's no account to make: you log in with GitHub.

README — the repo's own words, folded up so the grading fits on one screen

The Coffer — OSRS Grand Exchange flipping tool

Live: https://bensumm.github.io/The-Ledger/

A self-contained web app for finding and managing OSRS Grand Exchange flips. Vanilla JS, no build step, no framework, no bundler — plain static files served by GitHub Pages. Installable as a PWA (works on desktop and iOS home screen), but primary development is now on desktop.

Every price/margin in the app is after the 2% GE sell tax (floored per item, capped at 5m, none under 50gp). Price convention follows flipper usage: Buy = the instasell price (where you place buy offers), Sell = the instabuy price.

What it does

  • Finder — ranks flippable items by a budget-aware rating (profit/hr × a quality dampener blending ROI, liquidity, stability, and turnaround; hover the Risk grade for the per-factor breakdown). Stability here is a cheap live-price-vs-guide proxy — the full regime-drift check lives on the Trends page. Sortable by rating, profit/hr, margin, ROI, or volume. Typing a search query reveals every mapped match — including cheap items (soul rune ~300gp) the browse-mode price floor normally hides; those search-only rows show for the rating columns and lean on the quote button + star.
  • Trends — deep per-item analysis. A live "Suggested plan" (instant buy/sell + patient pricing that sizes a wider-margin offer off the recent 2h range), a regime-shift guard that warns when a recent price-level jump makes the hourly-timing stats unreliable, a plain-language guide-divergence readout ("Why this trend?"), 3-month price history, and a collapsible timing & seasonality section gated on a walk-forward backtest (hourly charts only appear when the timing edge is actually proven out-of-sample).
  • Watchlist — star items to track their live margins each refresh; the same starred set is the repo-shared watchlist the pipeline scans. (The old Signals tab — live cheap-window buy signals — was removed in 0.57.0; Ben didn't use it.)
  • Ledger — per-item grouped open/closed positions with after-tax realized/unrealized P/L, summarized in the "Coffer" header tiles. Item names link to Trends; multi-lot groups expand via a chevron; a "P&L by" Day/Week/Month control on the Closed-flips header drives a period strip whose buckets click to filter the table by sell date; the manual-entry form is a collapsible section and the closed columns are sortable.
  • Fill-data pipeline — see pipeline/ (below): captures real GE trades from RuneLite to fills.json so the tool can eventually calibrate its predictions against actual fills.

Files

  • index.html — the app shell (markup only)
  • styles.css — all styles
  • js/ — app logic as ES modules: state.js (shared mutable state as one STATE object + constants + persistence + diagnostics), money-math.js (the tax/margin/bond MATH — canonical tax()/netMargin/netMarginQty + the ONE BOND tax exception BOND_ID/isBond/bondFee: a bond is tax-exempt but pays a 10%-of-guide retrade fee, so netMargin(low,high,{bond,guide}) = sell − (buy + fee), tax-free; plus the generic clamp/now), money-format.js (gp/number DISPLAY formatting — fmt/fmtSig/fmtP/fmtTurn/fmtHour/fmtHourRange + ukHourOffset/localTzAbbrev/pad2/parseGp/sgn/grade/gradeCls; split out of the old format.js in the R2 rename. fmtHourRange(startH,endH) labels a diurnal hour-of-day window in BOTH the runner's local zone AND the UK — e.g. 01:00–03:00 PDT / 09:00–11:00 UK — so peak/dip window narration can't slip between the Pacific-local hours the tools emit and the UK-driven demand basis; the offset is Intl-derived per instant, DST-correct for both zones), charts-static.js (static inline SVG — svgLine/svgBars, fixed-size, no interaction; still used by the Trends hourly seasonality charts + the quote sparkline), charts-interactive.js (CL — the reusable interactive SVG chart: createChart(container,{series,overlay,fillBetween,refs,bands,markers, kind,yFmt,xFmt,spans,span}){setSpan,destroy} handle, with pointer-drag PAN, wheel/pinch ZOOM about the cursor, a span selector, y-axis auto-rescale to the visible window, a hover tooltip + crosshair, and a DEFAULT_SPANS export. Max zoom-in is density-floored (~4 sample points, medGap*4) so you can't zoom into empty space; explicit span buttons bypass that floor to hit their exact duration. Degrades to a no-op on a missing container or empty series. An optional overlay second line + fillBetween shading draws the Forward-forecast low/high CONE (0.61.0; absent config ⇒ byte-identical single-series). Consumed by trends.js — the Recent movement (2h), Price history (1/7/30/90d windows), Diurnal timing (7d/28d toggle), and Forward forecast (cone) charts; more surfaces adopt it over time. ADDITIVE — charts-static.js stays intact), marketfetch.js (shared browser fetch layer — one timeout-guarded jget
    • one cached fetchTs/fetch24h store, A2), market.js (price/guide fetch + scoring; keeps the bond in the catalog — searchable — with a bond-aware Finder margin via bondMarginOpts; AP4 desirabilityOf computes the Finder's shared rank + Desirability grade off js/estimators.mjs+js/rating.mjs. vol24Of(id) (0.74.2) is THE ONE HOME for turning STATE.VOL24 into a daily volume — every reader goes through it so the absent-vs-unavailable rule cannot be re-derived per call site: an item ABSENT from a PRESENT /24h map traded ZERO (measured — 0.0% of absent items traded during the day the map covers), while a null return means the MAP is unusable and the consumer must keep its wide prior. Consumed by desirabilityOf and by trends.js's item header), trends.js (archive + seasonal analysis + regime/patient/backtest — renders the Trends view; pure analytics live in trendcore.js; TV — also renders the Diurnal timing section via the shared windowread.mjs hourProfile/deriveDiurnalRange + a charts-interactive.js bar chart + an inform-only validate.mjs reachValidator note — the same computation the console prints), trendcore.js (TC1 — pure DOM-free Trends analytics: hourly/seasonal decomposition, the walk-forward backtestPlan gate, patientTargets offer sizing, bestWindow/median; moved out of trends.js for pipeline/test/trendcore.test.mjs), quotecore.js (DOM-free quote model + canonical market-table cells — computeQuote/regimeDrift/quoteCells; shared byte-for-byte with the node analysis scripts; also home to recentDirection (DP1), flushSignal (DL2 — the reactive liquid-flush firing read, consumed ONLY by pipeline/commands/watch-positions.mjs --dip, no app import), and nominateDip/reconcileDipPool/pruneDipPool (DL4 — the scan's flush-SUITABILITY nomination + the quality-ranked, self-pruning pool write; selectNominations is the legacy dedup/cap, retained + tested but no longer on the write path — all consumed ONLY by pipeline/commands/screen-flip-niches.mjs, no app import); also the ONE type-7 quantile/median home (SF-1): quantileSorted (pre-sorted input) + quantileOf/median (sort a copy) — termstructure.mjs re-exports it as quantile, retrojoin.mjs aliases quantileOf; and home to grossFromNet (PLAN-SALE-LOG-TAX — the EXACT sell-side inverse of tax(): the smallest integer g with g − tax(g) === round(net), found by a ±2 scan around round(net/0.98) then net + TAXCAP (the cap region falls out of tax saturation). NOT breakEven (that answers ≥ and lands 1gp high where two consecutive g share one net — the per-item floor makes ~every-50th pair collide, so an ask at an exact-2% point recovers 1gp low: display-only, realised never routes through it; fractional net inverts the rounded value). The FALLBACK gross-recovery for .json-era net worth in the reconstruction + the raw-row display sites — since SLT C5 those prefer the plugin's own recorded tax (worth + tax, exact) when the row carries it; bond-blind like tax() — the quarantined-bond caveat applies), forward-reach.mjs (the shared FORWARD-SCORING primitives over the 1h archive — touchedAt (bid side, avgLowPrice), reachedWithin / maxHighWithin (ask side, avgHighPrice), covers, firstIndexAfter, and the END-OF-WINDOW bail pair endLowWithin / endHighWithin off one lastPrintWithin(series, from, windowH, field) (chunk 4 homed them here; js/reach-surface.mjs had carried a private endLowWithin twin). The two bails are DIFFERENT POLICIES for a seller whose ask was never reached — cross the spread into a standing bid (avgLow, chunk 1's choice) or rest at the ask level (avgHigh) — and join-exit-ev.mjs measures that they rank contenders differently, so a consumer must say which it charges rather than inheriting one. Lives in js/ as of PLAN-REACH-SURFACE chunk 0 (it was pipeline/lib/market/forward-reach.mjs; that path is now a one-line re-export shim so every pipeline importer resolves byte-identically, the pipeline/lib/signal/estimators.mjs pattern). The move is what lets js/reach-surface.mjs build the surface from the same walk the joiners score with — js/ never imports pipeline/, so a primitive both sides need has to live here. covers is the load-bearing one: an unresolved window is DROPPED, never counted as a miss, because counting it biases every rate DOWN by exactly the truncation at the end of the archive. Side convention is pinned by js/quotecore.js — a BUY fills against avgLow, a SELL against avgHigh. Pure: no fs, no fetch, no clock; the caller supplies the ts-ascending series. ⚠ It is shared by join-asym-outcomes.mjs + join-reach-outcomes.mjs + js/reach-surface.mjs ONLY: join-reach-basis.mjs still carries its own independent scoreForward walk, and pipeline/lib/market/printed-at.mjs a third — so a fix here does NOT reach every reach joiner. Consolidating them is unfinished work, not a claim to repeat; printedAt additionally has the better tristate (null when no bucket exists, which reachedWithin collapses to false)), reach-surface.mjs (PLAN-REACH-SURFACE chunk 1 — the empirical reach surface p(ask, H) for ONE item, replayed from its own 1h archive. buildReachSurface(series, opts) walks origins every strideH hours and scores a z-grid of ask levels against maxHighWithin, returning the p(z,H) grid plus refHigh/disp, bailZOnMiss (the miss payoff, in z), the per-horizon refusal, and a reported-never-applied 1h-vs-5m grainBias. surfaceProb(surface, ask, H) reads a live ask off it; surfaceShape reduces the curve to z50/z20/spread; referenceAsOf exposes the point-in-time reference. It re-derives nothing: refHigh is windowread.mjs's recentQuant(days,'ask',0.5,3) and disp its iqr (exported from that module for this — it was module-local), both fed by windowStats, whose pt.timestamp shape is bridged from the archive's .ts here rather than in windowStats, where every other caller already passes timestamp. Everything is @provisional-api until chunk 3's read-exit-surface.mjs consumes it — chunk 4 scores the surface before anything prices off it. Four properties are load-bearing and each has a killed mutant in pipeline/test/reach-surface.test.mjs: (1) no look-aheadrefHigh/disp are re-derived at every origin from complete days strictly before it, so a level scored 90 days ago never sees day 91; (2) unresolved windows are DROPPED, never scored as misses, and an origin whose window held no printing bucket is dropped too and COUNTED (noPrintDropped) because the archive cannot tell a quiet hour from an unfetched one — that drop biases p UP; (3) refusal is a WIDTH bound — a cell is thin by its Wilson half-width (Wald reads 0 at p=0 and would price an empty cell as certain), computed on nIndep (origins thinned to non-overlapping windows) rather than the raw count; a HORIZON refuses on its decision cell, the one nearest p=0.5, because "every cell thin" can never fire; (4) the grain bias is reported, never applied — passing fiveMin must not move a grid cell, and fiveMinCoverage rides beside every delta since a ~0 delta on a thin item means NOT MEASURABLE rather than unbiased. The z axis is (ask - refHigh)/disp, not a % grid: a raw %-above-median grid inverts the trend split. z-monotonicity needs no cleanup — it holds by construction (one origin set, one threshold per origin; measured 0 violations in 22,500 adjacent pairs, so the specified isotonic pass was deleted rather than shipped inert); the H axis DOES invert because its origin set shrinks with H (155 violations over 250 items, max 12.1pp) and keeps a running max. REACH IS NOT FILL — queue position is invisible in bucketed aggregates, so p bounds P(fill) from ABOVE as a working bound, not a theorem: the folded plan's §6.1 RETRACTED the theorem reading — the 1h-average instrument pushes p the other way on liquid items, so the net error's direction is item-dependent there (read it off grainBias, never assume it) — and every consumer must say so. What chunk 1 MEASURED — including that PLAN-REACH-SURFACE §1.5's taxonomy premise did not survive and its ordering inverted — is §1b of the folded plan (git show bdea911:plans/PLAN-REACH-SURFACE.md, folded into PLAN.md 2026-08-30); don't restate it here), exit-ev.mjs (PLAN-REACH-SURFACE chunk 2 — the inversions that turn a reach surface into a PRICE. evCurve(surface,H,{bailNet,delayCost}) scores every level of one horizon at EV = p·net(ask) + (1−p)·(net(bail) − delayCost) (tax from money-math.js, the ONE definition); askStar is its argmax, askForHorizon the highest level clearing pTarget, and horizonForAsk the SMALLEST horizon clearing it — returned with the full p-by-H row, so the threshold never travels alone; p is read through reach-surface.mjs's surfaceProb, the ONE interpolation, so every row carries its ciHalf. Six properties are load-bearing and each has a killed mutant in pipeline/test/exit-ev.test.mjs: (1) EV has an interior maximum on real curves — the property the co-log scorer lacked, and without it nothing can rank; (2) the miss payoff is PER-CELL (bailZOnMiss) because that is the conditional expectation the decomposition asks for — and its MEASURED DIRECTION is the opposite of the intuition: at a low ask only a catastrophic window misses, so E[bail|miss] is WORST there and rises toward the unconditional value as the ask climbs, which means per-cell prices at or ABOVE the unconditional form, never below (an earlier version of this entry, the module header and the test all asserted the reverse); (3) delayCost is charged to the MISS branch only — on both branches it is a constant at fixed H and cannot move the argmax at all, so the asymmetry is what makes waiting cost anything; (4) a maximum on the last SCORED z is a refusal, not a price (the edge is the scored one, not the declared grid — a dropped cell would otherwise pass as interior); (5) net() is the ONE tax definition on BOTH legs — a tax asymmetry is 2% of price, larger than the whole EV spread being optimized over; (6) horizonForAsk reads horizons ascending regardless of grid order. Consumed by chunk 3's read-exit-surface.mjs, which is why the @provisional-api markers are gone from every export but askForHorizon — that one is now @test-only and has no production consumer BY DESIGN, since §1c forbids its ask as a price. askStar is an argmax over a PLATEAU, not a point — adjacent cells sit within a few basis points of refHigh of each other, decided off a p known to a few pp, so a consumer must present a band and never a false point; read-exit-surface.mjs owns that. The chunk-2 stop-or-go gate and what it measured live in the folded plan's §1c — PLAN-REACH-SURFACE folded into PLAN.md 2026-08-30; full text git show bdea911:plans/PLAN-REACH-SURFACE.md (don't restate its numbers here — they were, once). The one rule to carry out of it: pTarget must never pick a price. It answers "how long", never "how much". ⚠ And chunk 4 has now measured what askStar is worth: scored against realized net gp over the archive it LOSES to a deployed incumbent, decisively enough to fire the plan's pre-registered null branch. Everything above is still how the arithmetic works; none of it is a reason to price an offer off askStar. See join-exit-ev.mjs), windowread.mjs (P2 — pure window-range/reach math: windowReliability (DT4, 2026-08-10 — the split-half hours gate: parity-split the last WINDOW_RELIABLE_NIGHTS (14) days, hourProfile each half, Pearson-correlate the de-trended devLow/devHi 24h vectors, gate on min(rLow,rHi) ≥ WINDOW_RELIABLE_R (0.6, PLACEHOLDER). Returns a TRI-state reliable true/false/null — null = not measurable, deliberately distinct from a measured fail. Pins its OWN 14-day window off the RAW series, never the caller's nights. Consumed by diurnalTimedLap (lap.reliable), formatTimedLap, softBuyRead/softBuyHoursClause, the /scan digest soft-buy cell, read-schedule.mjs and read-window-range --profile; shadow-logged by suggestlog.mjs. DISPLAY-ONLY — it gates no grade, rank, verdict or screen.json field. It DOES move displayed prices on the ~0.8% of rows that PASS: DT4b refits those over the gate's window, and hours and levels come from one fit, so the soft-buy floor/diurnal levels shift there — see displayFitNights) + softBuyHoursClause (the ONE wording for the three hours states, full + compact styles) + windowStats/quantLow/quantHigh/touchedDays/reachedDays (EC2 2026-09-08: windowStats also returns forming — today's partial-day {key, low, hi, volLo, volHi}, still excluded from days as always but no longer silently discarded; floorCeilingTrack's monotone-certainty read consumes it) + the RC1 recencySplit/recentQuant reach-contamination guard + askExitRead (PLAN-POSITIONS-WINDOW-READ 2026-07-18 — the ONE ask-side "typical exit" assembly: daily-HIGH q50/q75/every-day levels + the scored list-price reach/placement + the ≥FIVE_MIN_MIN_DAYS 5m-grain reach; pure over already-computed windowStats results, so read-window-range.mjs's --ask block and quote-items.mjs --positions' auto-surfaced big-ticket ↗ windowExit note render from ONE definition instead of re-sequencing the primitives) + reachMargin (the fade check, 2026-07-20 — folded INTO askExitRead, so it rides both surfaces: the cushion TREND fading|stable|extending over the recent MARGIN_NIGHTS days at MARGIN_FADE_FRAC, the current-day cushion, and today's pace — live-now vs the reaching-day median at this hour-of-day off the in-hand hourProfile; symmetric ask/bid, inform-only, placeholders pending F1; the lean summary rides suggestions.jsonl via windowExitShadow) + avgBoundRead/formatAvgBound (2026-08-05 — the DEEP-BOOK reach-misread guard: touchedDays/reachedDays count days the per-day extremum of 1h-bucket AVERAGES crossed a level, a bias that is strict IN PROPORTION TO LIQUIDITY, so a low N/M on a deep book means "below every hourly average", not "never fills". Fires only when the limiting-side volDay clears REACH_RELIEF_MIN_VOL (reused from js/estimators/reach.mjs, never forked — passed in as an opt so windowread.mjs stays a leaf) AND the hit fraction is below AVG_BOUND_LOW_FRAC; names the averaged basis, the signed gap to the most extreme daily average (read against AC2's ~0.36–0.56% measured smoothing bias), the in-window competing pool, and the bid-side "low placement = deep patient entry" gloss. Rendered by read-window-range.mjs on every scored --bid/--ask/--exit AND mirrored into the --json/--out dump as avgBound (the machine path is the point — agents read verify.json). THIN books return null and print byte-identically, an asymmetry pinned by test in pipeline/test/windowread.test.mjs. INFORM-ONLY, n≈0 — gates nothing) + the hour-of-day diurnal profile hourProfile/deriveDiurnalRange (2026-07-09 — de-trended per-hour dip/peak detection, side-specific clustering, and the stale-to-live guard; the peak-timing engine screen-flip-niches.mjs auto-runs and windowrange --profile prints) + multi-peak windows (PLAN-MULTI-PEAK-WINDOWS 2026-07-23 — hourProfile gains ADDITIVE prominence-ranked peaks/dips arrays (length 1–2); peaks[0]/dips[0] deep-equal the unchanged peak/dip, peaks[1]/dips[1] is a SECOND window that cleared the SECOND_PROMINENCE_FRAC topographic-prominence gate; diurnalTimedLap mirrors it with index-aligned askReaches/bidReaches arrays — inform-only, n≈0, rendered as a trailing clause by emit.mjs formatTimedLap) + computeReality/realityClause (PLAN-DIURNAL-RECENCY-GUARD 2026-07-24 — each emitted peak/dip carries an additive reality level-check: spikeTop (a recent 1–2 day spike over-generalised into the quoted level) / staleOptimistic (an old high the current regime no longer reaches) + a recency-honest typicalLevel to quote instead, off the cluster's per-day HIGHS/LOWS with ZERO new fetch; realityClause is the ONE renderer across nine console call sites as of Chunk 2c (2026-08-13) — --profile window headers + its → BID/ASK line, the non---profile diurnal: summary, formatTimedLap's primary BID/ASK and its also ASK/also BID secondaries, quote-items' windowExit peak-level bit, the held-lot thesis-frame exit, and /schedule's Level column. (This cell read "the three console surfaces" through Chunks 2b AND 2c; every hand-enumeration of these sites has been short — treat the number as a floor, and see the plan's §10 for what is still bare.) Constants SPIKE_REACH_FRAC/SPIKE_PLACEMENT_PCTILE/ SPIKE_MIN_GAP_FRAC/REALITY_TYPICAL_QUANT/REALITY_TYPICAL_RECENTN are PLACEHOLDERS; inform-only, n≈0, never gates — the emitted level is unchanged. No longer console-only: Chunk 2c added the WRITE side, so reality now reaches suggestions.jsonl (timedLapShadow) and verify.json (result.profile, which already carried it) — that is what finally makes "does this flag predict anything" an answerable question) + hourConcentration/diurnalTimedLap (PLAN-DIURNAL-TIMING DT1 2026-07-23 — hourConcentration is a per-day argmin/argmax-hour CIRCULAR-concentration classifier (mean resultant length R∈[0,1] of each day's own trough/peak hour, HOURCONC_MIN_DAYS/HOURCONC_MIN_R placeholders), distinct from hourProfile's aggregate cluster width; diurnalTimedLap is deriveDiurnalRange's output EXTENDED with net/roi (the TIMED trough→peak lap, netMargin from js/money-math.js) + instantNet/instantRoi (the SAME-HOUR/churn margin — both surfaced, since a big-ticket item can show a NEGATIVE same-hour margin beside a POSITIVE timed one) + bidReach/ askReach (recencySplit scored against the chosen dip/peak levels' own windowStats slice) + lowTrend/hiTrend (projectTrajectory) + dipPool/peakPool + trancheComfort/ trancheCeiling (DT_TRANCHE_COMFORT_VOL_PCT=0.5%/DT_TRANCHE_CEILING_VOL_PCT=1% of volDay, borrowed from js/estimators/reach.mjs's n≈6 reach-relief knee, not validated for diurnal specifically — rendered tranche ~X clean · ~Y price-knee, and read the two disclaimers on emit.mjs's caveat before quoting either number: it is a price-degradation knee rather than a clearing cap, and a ROUND-TRIP bound rather than a per-leg one, both of which have been misread off it in a live session) + hourConcentration's clean verdict; degrades to {degraded:true, reason}, never a throw. DT2 (2026-07-23) wires this into screen-flip-niches.mjs for EVERY flip-niche survivor (was top-picks-only via raw hourProfile+deriveDiurnalRange), rendered through the ONE shared pipeline/lib/render/emit.mjs formatTimedLap — see that file's README entry. DT3 (2026-07-23) wires the SAME diurnalTimedLap+formatTimedLap pair into quote-items.mjs's bare-quote kind:'diurnal' note (prof/dr themselves stay — they still feed extraEst.diurnal, the window-clear peak window, and the forward E4 inputs), and swaps watch-positions.mjs's two direct hourProfile+ deriveDiurnalRange call sites (the shadow-log bid/ask co-log, the diurnalAsk cycle-fallback exit) for diurnalTimedLap — those two are VALUE consumers, not note-render sites, so only the underlying computation moved) + softBuyRead/formatSoftBuy/SOFT_BUY_CUE_TEXT (2026-07-22 — the ADD-while-holding soft-buy timing read off the SAME hourProfile: the dip-cluster FLOOR level + a live-vs-dip-floor @floor/+X% marker at SOFT_BUY_AT_FLOOR_PCT, ending in a cue, with the diurnal DIP window in a trailing parenthetical labelled attended. DT2 (2026-08-09) made the render LEVEL-FIRST and re-scoped the window: it does NOT time a resting offer (71.2% in-window touch vs 70.5% for a random window of the same width; waiting forfeits ~29% of bid fill-days at an identical price), so a resting bid is placed at the level now and the hours are for attended market-TAKING. The @floor cue is FLOOR-AWARE (the fang under-read fix) — softBuyRead takes an optional fc (a floorCeilingTrack result the caller already computed; NO re-derived slope) and resolves buy now (flat/ranging), ▲ favorable (rising floor — price-trend-only, never a green-light), or ▽ caution — floor breaking ↓ (broke/crash-risk — a dump artifact, not a discount); SOFT_BUY_CUE_TEXT maps the cue to its wording so both surfaces phrase it identically. quote-items.mjs renders it as the ⏳ softBuy note on held lots + bare quotes (threading the fc pushTrajectory returns), and screen-flip-niches.mjs's digest soft-buy column delegates to the SAME helper (ONE implementation); inform-only, n≈0, null profile / no fc ⇒ plain cue) + asymPair (PART II PLAN-GRADE-REACH 2026-07-12 — the day-level deep-bid/high-reach-ask realizable pair + P_ask/P_bid, consumed by js/estimators.mjs asymEstimate for the ◆ asym fill inform line + the asym suggestions-ledger shadow field) + clearableAsk (PLAN-DEPTH-EXIT DE1 2026-07-15 — the percentile-DEPTH exit: reconstructs a per-day price→volume distribution from the 1h bucket point masses and answers "what can I actually BOOK at?" for a given lot size; the reach count is its qty→0 limit, and a thin book collapses to a null-with-reason; feeds the --depth "BOOK AT ≤X" line + the LIVE DE3 depthExit shadow on watch/quote held lots; the DEPTH_* constants module-internal placeholders. depthDays/clearableBid were REMOVED 2026-07-22, PLAN-REMOVE-DEPTH-PRESSURE-READS — git-revivable) + demandPressure/reachableBand (PLAN-DEPTH-EXIT Extension A PB1 2026-07-15 — the pressure-driven reachable band: s=ln(medVolHi/medVolLo) sets each side's headroom base ± band·φ(±s)·reliability off the recent central daily level (RC1 reused)
    • the daily-high/low IQR; a thin-VOLUME book collapses to the smoothed center via the sample-reliability guard (no peak-cap); the PRESSURE_* constants are exported n≈0 placeholders and the Soul-rune/sell-heavy reasonableness pins live in the test; its surviving consumers are the co-log shadow (bid/band), the read-window-range.mjs --pressure inspector and reverse-flip's reverseListBand — the pressure SELL model it once priced was RETIRED 2026-08-30 (join-exit-ev.mjs's criterion; CHANGELOG 0.76.0). The Extension-B hourlyPressure/demandRegime per-hour demand-cycle classifier was REMOVED 2026-07-22, PLAN-REMOVE-DEPTH-PRESSURE-READS — git-revivable) + trajectoryRead (2026-07-21, the fang under-read fix — the shared multi-day SHAPE read over a windowStats().days series: classifies rising/falling/oscillating/based/elevated + the window floor/ceiling (with the day each printed) + where a liveRef sits between them; HEURISTIC/inform-only, never gates. Its rendered ⌁ read: line was RETIRED at both emitters by R6floorCeilingTrack below supersedes it; what survives is the floor/ceiling/livePos fields that note now carries) + floorCeilingTrack/formatFloorCeiling (PLAN-DRIFT-VS-CRASH, 2026-07-22 — the phase-aligned floor+ceiling slope-asymmetry classifier that trajectoryRead's single min-low/max-high collapse washes out: reads the daily-LOW track and daily-HIGH track SEPARATELY, each a recent-window least-squares slope (windowed, so a short trailing wiggle cannot flip it; a volatile END day can — OLS endpoint leverage) classified rising|flat|falling + a raw-sign trailing micro-run for duration, plus a discrete floor-break flag (latest completed low vs the prior-lookback floor); combines the two slopes + the break into crash-risk (break dominates) / healthy-trend / compressing-up / mild-cooldown / cooling / ranging. REQUIREMENT #1 phase-alignment: the forming/incomplete day is DROPPED from the slope/break and surfaced separately as provisional. EC2 (PLAN-ENTRY-CONFIDENCE, 2026-09-08 — the Avernic-hilt entry post-mortem): the live forming read arrives via the forming opt (= windowStats().forming, the partial-day aggregates windowStats always excluded from days and now RETURNS instead of discarding — before this, every production days was pre-stripped, so fc.forming was structurally null and the "provisional" clause never rendered anywhere); todayKey survives as the hand-built-days fallback. On it rides formingContradiction, the MONOTONE-CERTAINTY read: a forming day's low only falls, so forming.low < a completed-days statistic is decided at any hour — under-trough (forming low < min(prior floor, latest): the discrete break is CERTAIN at day end) or under-last-low (the floor label is STALE — measured over 21,013 archive item-days at 21:00, the next 3 days print below the current floor 88.5% when it fires vs 53.1% when it doesn't; a "cheaper entry likely" read, NOT a trend-reversal predictor — forward slope is unpredicted either way, and 44.1% of rising labels carried the contradiction). formatFloorCeiling stale-marks a rising-flavored label on under-last-low and WITHHOLDS the label on under-trough; softBuyFloorCue maps under-trough → caution and rising+under-last-low → the new stale-uptrend cue. The regime GATE (quotecore.js regimeDrift), driftExitFrom's slope read and join-exit-ev.mjs's replay deliberately pass no forming — gates and replays stay completed-days-only. HEURISTIC/n≈0/inform-only, never gates; formatFloorCeiling (fmt injected — windowread stays dependency-free) is the ONE line-render both read-window-range.mjs's trajectory block and quote-items.mjs's note use); MOVED here from pipeline/lib/ so it is node- AND app-importable like quotecore.js; consumed by pipeline/commands/read-window-range.mjs, pipeline/commands/watch-positions.mjs, pipeline/commands/screen-flip-niches.mjs, js/validate.mjs and js/forecast.mjs (both now app-imported via js/trends.js, TV). PF1 (2026-07-10) added additive per-hour dispersion fields devMid/devLowSpread/devHiSpread (IQR of the deviation samples) so the forecast band isn't re-derived; every pre-existing field is byte-identical), forecast.mjs (PF1 2026-07-10 — the pure forward 12h/24h price projection: CONSUMES an hourProfile object and produces a diurnal+trend forecast — diurnalForecast(profile, ctx)nextTrough/nextPeak {level, band, etaH, window, confidence, mode} + the per-hour projected series, plus whenBuyable/ whenSellable/fmtEta. The interpretable ADDITIVE model projLevel(h) ≈ baselineNow + trendPerHour·Δt + deTrendedHourShape(h); anchor from the live quote, shape/dispersion from up to 14d, trend from the recent slope. DEGRADES LOUDLY to {forecast:null, reason} on a spike/decay phase, a live band violation, a thin/short series, an unreliable quote, or a trend-erased dip (trend-only mode); the band widens with horizon. Claims ONLY "recurring diurnal shape + dumb trend extension" — never an exogenous shock. Imports only windowread.mjs (no quotecore — phase/mom/reliable arrive as plain ctx). INFORM-ONLY / console-only / provisional (n≈0, every constant a NAMED PLACEHOLDER pending the PF8 backtest); no consumer wired yet (PF2–PF8) and no app import → no APP_VERSION. Pinned by pipeline/test/forecast.test.mjs. Also homes PLAN-OSCILLATION-CYCLE Chunk 1 (the multi-week oscillator lane): driftAdjustedExit(fc, {ceilingSlope,floorSlope,holdHorizonDays}) composes diurnalForecast's next trough/peak with a multi-week drift NUMBER (never a direction label — NO phase/direction field), shifting ONLY by the RESIDUAL horizon past the diurnal eta (diurnalForecast already trend-extrapolates to the eta), and oscillationVsKnife(days) — a detector (REDESIGNED at F-A, 2026-07-22 — the original first-difference flip-fraction metric measured day-to-day NOISINESS, not harvestable oscillation, and mislabeled fang/blowpipe's smooth multi-day runs a false knife; see the header comment above the function for the full finding) that detrends the daily mids (same shared projectTrajectory slope, one-home) and splits the residuals into maximal same-direction LEGS, counting a leg as real only past OSC_MIN_LEG_DAYS + OSC_AMP_NOISE_MULT× the series' own day-to-day noise floor; oscillating fires at OSC_MIN_LEGS (≥2 direction reversals) — fewer legs is a monotone linear-fit hump (even a CURVED collapse), never a real cycle. Tells an oscillating-while-drifting shape (fang/blowpipe) from a monotone knife where floorCeilingTrack.oscillating structurally can't. F-H (2026-07-22) added OSC_DETECTOR_NIGHTS=21 (> the amplitude gate's AMP_NIGHTS=14): renderAmplitudeMode feeds the detector its OWN longer trailing windowStats(...).days window (off the same in-hand series, NO fetch) so it sees the ≥1.5 cycles / ≥3 legs it needs WITHOUT widening the gate's AMP_NIGHTS daily-range/ reach/recency read — a sample-size fix BOUNDED by the ~16-day /timeseries?timestep=1h endpoint, not a calibration. ⚠ That endpoint bound is now load-bearing, not incidental (2026-08-11): OSC_MIN_LEGS is an ABSOLUTE leg count with no length normalisation, so the label is a function of WINDOW LENGTH — measured 59.5% OSC at 14d → 99.9% at 60d on the real archive, and ~66% at 14d → ~100% at 30d on a synthetic DRIFTLESS RANDOM WALK containing no cycle at all. Amplitude cannot matter — every threshold is homogeneous of degree 1 in price, so the criterion is scale-free BY CONSTRUCTION and length is the only free variable (an earlier note offered amplitude-invariance as corroborating measurement; it is an algebraic identity, not evidence). The ~15d cap is the only reason the knife temper still discriminates; feeding the detector a deeper archive.mjs series (F-H calls this a noted-not-built follow-up, and renderAmplitudeMode already has that archive open) would take it to ~100% and silently delete the guard. Normalise the criterion BEFORE widening the window — full note in the function header; measured in RANGE-PERSISTENCE-FINDINGS.md. INFORM-ONLY, wired into NO gate in Chunk 1 (gating is Chunk 3); pinned by pipeline/test/oscillation-cycle.test.mjs. Chunk 2 adds driftExitFrom(profile, days, ctx, opts) — the ONE slope-sourcing + drift-adjusted-exit COMPOSITION (imports floorCeilingTrack from windowread to pull the ceiling/floor slope off an in-hand windowStats().days, NO fetch; builds the diurnalForecast wrapper; calls driftAdjustedExit) — the reusable caller pattern the amplitude lane established and Chunk 6 REUSES; PURE/tax-free (the after-tax margin stays the caller's concern). Pinned by pipeline/test/oscillation-shadow.test.mjs. Chunk 3 (THE ONLY GATE) turns that margin into amplitudeGate's margin-below-floor reject (amplitudeDriftMargin().margin <= 0, direction-agnostic, the floor already inside the margin) sequenced after trend/knife, computed ONCE at the gate stage in renderAmplitudeMode and reused for the shadow-log, and TEMPERS the knife guard with oscillationVsKnife (a drift-riding oscillator is not a false knife → falls through to the margin gate). Pinned by pipeline/test/oscillation-gate.test.mjs. Chunk 6 REUSES driftExitFrom per-thesis (band/churn/scalp/value drift-adjusted-exit INFORM notes — see js/flip-niches.mjs; console-only, no gate). Chunk 5 folds the drift-adjusted exit LEVEL into the SHARED formatFloorCeiling note path (an optional drift opt — the caller passes a pre-computed driftAdjustedExit() result off its in-hand prof+days, so windowread keeps its one-way arrow) so it rides beside EVERY price suggestion (quote-items.mjs trajectory note, read-window-range.mjs, and — APP-VISIBLE — js/trends.js renderForecast); a projected LEVEL never a direction verdict, display-only, degrade-clean; APP_VERSION-bumped (reaches js/trends.js). Pinned by pipeline/test/oscillation-render.test.mjs). F-F adds a trough-vs-decay DISPLAY annotation to the amplitude reach cell (reachPhaseNote in screen-flip-niches.mjs — recent+full both-leg reach plus a 3-signal phase note off oscillating/floorSlope/margin, direction-agnostic in the knife bucket, console-only; pinned by pipeline/test/oscillation-reachphase.test.mjs), validate.mjs (P2 — the pure VALIDATOR REGISTRY (ctx)→{status:pass|caution|reject,reason,evidence} run on EVERY surface: reachValidator wraps windowread reach + RC1 into caution/reject WITH the reach evidence; floorValidator (P3, BUY-side) rejects/cautions a buy parked above the durable floor — and durableFloorRead(vres) (2026-08-06) is the ONE canonical extraction of that verdict ({status, ranges, lookback}) for consumers that need the LEVEL read without re-deriving it: the soft-buy unproven-base cue, the ⚠N×floor row probe, and the bucketed caution footer all compose it rather than fork it (windowread cannot import validate — the arrow is one-way — so the caller hands it down); trajectoryValidator (TV1 2026-07-09, BUY-side) is the SHAPE policy over termstructure's classifyTrajectory — knife/oscillating/based/elevated; valueAmplitudeValidator (TV1) the recent-week amplitude+proximity for value; limitValidator (LM1) the rolling-4h buy-limit; dipPostureValidator (DP1, BUY-side, INFORM-only/NEVER-REJECT) the dip DIRECTION read via quotecore.js's recentDirection (a reverting dip → caution "cross or pass"; wired inform on band/churn). All degrade to pass on missing data and never throw. runValidators(ctx,{specs}) drives a PER-THESIS plan ({key,mode,window} from js/flip-niches.mjs) — gate (verdict stands) vs inform (informFlags: annotate-only, clamped to pass, would-have verdict logged via leanValidators). worstStatus/flags. Screens DROP reject + FLAG caution + SHOW inform notes; explicit asks/held/watchlist never hidden. App-imported via js/trends.js (TV) — a behavioral change here needs the smoke test + the APP_VERSION rule), termstructure.mjs (P3 — pure DOM-free multi-day term structure over a daily-mid [{ts,mid}] series: the 1/3/7/14/28d termStructure (median/low/high/pctInRange per lookback), a durable floor (low quantile of the longest multi-week lookback), a robust ceiling (P5 — the symmetric high quantile q85, so a lone spike can't inflate a range), a typical fluctuation (IQR), and a trajectory SHAPE (TV1 — classifyTrajectory: knife/oscillating/based/rising/elevated/flat, attached as ts.trajectory); degrades to hasData:false/unknown on a short series. Plus (DT6, PLAN-DIURNAL-TIMING §6, 2026-07-23) basePosition(ts) — a pure, LIGHT read of an already-computed ts: reuses ts.lookbacks[14].pctInRange (the SAME field classifyTrajectory already scores) + a 3-way coarsening of ts.trajectory.shape (+ts.recentTrend.dir to split a falling-drift oscillation as "decaying") onto range-bound/ trending↑/trending↓/decaying, for the screen-flip-niches.mjs Base position note on band/ churn/amplitude survivors (rendered by <

Read the rest on GitHub

Scan report · 2026-09-16
  • Prohibited terms or links
  • Repository eligibility
  • slopscore.md paperwork
  • Content policy
  • Risk review — +10 owner has 0 followers; +25 binaries at repo root (serve.cmd, watch-log.cmd)

From the balcony · 2 of 4 clapped

  1. Princessclapped
    Live demo at GitHub Pages, clear vanilla JS implementation, MIT license, declared working status, and detailed feature documentation showing functional OSRS market analysis tool.
  2. Crusoeclapped
    No vulnerable dependencies, self-contained web app with no telemetry or credential requirements, clear local-only data handling via GitHub Pages.

Cap'm Slop and Schnitzel read it and passed. Their reasons are on the balcony, with every other verdict.

Critics are accounts on this site with no GitHub account behind them. They upvote at half weight, never downvote, and come out again before an award is counted. Who they are.

0 comments

log in to comment.

report this listinglog in to report