arrow_backRetour aux issues
Wayfare-labs/wayfare
#36
Débutant
Ouvrirarrow_forward
Débutant
Ouvrirarrow_forward
Débutant
Ouvrirarrow_forward
Metric: bid/ask spread on the direct order book
ecoDébutant
help wanted
area:pricing
difficulty:medium
descriptionDescription
Written to the standard set by #33 — read that issue first for the expected level of detail and the acceptance criteria every check shares.
## What
Measure the bid/ask spread on the direct order book for a corridor, where one exists.
## Why it matters
The spread is the cheapest possible signal of whether a market is real. The USDC/NGNC book measured **128.8% of mid** on 2026-08-04 — a number that says "no two-sided participation" far more directly than a loss percentage does.
## Which layer
**Layer 2 — deterministic calculation** over layer 1 facts. It may not be more certain than the order book it is computed from.
## This is a metric, not a check
Read the "Checks and metrics are separate shapes" section of [docs/checks.md](https://github.com/Wayfare-labs/wayfare/blob/main/docs/checks.md) before starting.
This produces a **quantity**, so it returns `MetricResult` with `Value` and `Unit`, not `CheckResult` with `Passed`. Do not add a threshold turning it into a pass/fail — deciding what spread is unacceptable is a maintainer-owned judgement, and no such threshold exists yet.
## Exactly where the code goes
- **New file:** `checks/metric_spread.go`
- Tests appended to `checks/checks_test.go`
- `dex.BookHealth` in `dex/health.go` **already computes `SpreadPct`** — reuse it rather than reimplementing. This issue is largely about exposing it as a metric through the contract
## Where the data comes from
`dex.Client.OrderBook` (`dex/health.go`), which wraps Horizon's `/order_book`.
## What "unmeasurable" means here
- No order book for the pair, or one side empty → `Determined: false`, **never `Value: 0`**. A spread of nothing and a spread that could not be read are different facts, and zero is a plausible-looking number for the second
- Dust-only book → undetermined, with the reason naming it. `dex/health.go` already filters dust and explains why
## Acceptance criteria
- [ ] Returns `MetricResult`, not `CheckResult`
- [ ] `Unit` is `UnitPercent`
- [ ] `decimal.Decimal` throughout; **no float64** anywhere in the calculation
- [ ] Returns UNABLE-TO-DETERMINE (not zero) when the book is empty or one-sided
- [ ] Records evidence: the market queried and the levels observed
- [ ] Reuses `dex.BookHealth` rather than recomputing the spread
- [ ] Table tests from `dex/testdata/orderbook-xlm-ngnc.json` — a real captured book already in the repo
- [ ] Negative test: an empty or one-sided book
- [ ] No new third-party dependencies
- [ ] gofmt, vet, test -race, golangci-lint clean
- [ ] Does not change the headline integrity state or verdict thresholds
- [ ] **Adds no threshold** turning the metric into a verdict
## Out of scope
Nothing in `dex/` may change — `BookHealth` is maintainer-owned pricing code and this issue only consumes it. Nothing in `route/`, `sep38/`, `runstore/runstore.go`.
## How to verify locally
```bash
make offline-test
go test ./checks/ -run TestSpread -v
```
Issues similaires
medik8s/fence-agents-remediation
star18
Poids du dépôt moyen
v0.8.0 starts a cluster-scoped Secret informer despite namespace-scoped Secret RBAC
## Description While testing Fence Agents Remediation (FAR) v0.8.0, I observed the manager repeatedly attempting to li…
Go
good first issue
Wayfare-labs/wayfare
star1
Poids du dépôt léger
Check that an issuer's home_domain round-trips to the same stellar.toml
Written to the standard set by #33 — read that issue first for the expected level of detail and the acceptance criteria…
Go
help wanted
area:corridor
Wayfare-labs/wayfare
star1
Poids du dépôt léger
Report auth_immutable: whether an issuer's flags can still change
> **This issue is the reference standard.** Every other check issue links back here for the expected level of detail. I…
Go
help wanted
good first issue