# The Harmonic Risk Feed

One record per Robinhood stock token on Robinhood Chain that a contract reads in one call and a wallet reads free. It answers three questions at the block of the read: can the token be priced right now, at what mark, and is anything unusual happening to it.

- Contract: `0xd91e56a9999974a1319150cd6fa00bc87cc2d91d` (HarmonicRiskFeed) on Robinhood Chain
- Source: `GET /riskfeed/HarmonicRiskFeed.sol`
- Board: `GET /api/riskfeed`, one token at `GET /api/riskfeed/:symbol`

## The interface

```solidity
interface IHarmonicRiskFeed {
    function statusOf(address token) external view returns (uint8 status, uint128 mark, uint40 markAt, uint40 checkedAt, uint32 round);
    function markOf(address token, uint8 minStatus) external view returns (uint128 mark, uint8 status, uint40 checkedAt);
    function sessionNow() external view returns (uint8 session, uint40 endsAt);
    function canRead(address reader) external view returns (bool);
    function adapterOf(address token, uint8 minStatus) external view returns (address);
}
```

`riskOf(token)` returns the whole answer: status, session and when it ends, mark, source, markAt, checkedAt, bandBps, verdict and since when, the multiplier on record and as read now, the last step (kind, from, ppm, at), the latest split and fall, the writer's flags, the live flags, the Chainlink round and the round count.

## The lender read

```solidity
IHarmonicRiskFeed feed = IHarmonicRiskFeed(0xd91e56a9999974a1319150cd6fa00bc87cc2d91d);
(uint128 mark, uint8 status, uint40 checkedAt) = feed.markOf(token, 9); // reverts NotPriceable(token, status) below Extended
// mark: 1e8 dollars per token, the share times the multiplier, the unit the token's Chainlink feeds use
// status 9 is extended hours and 10 is live; 7 and 8 carry the last price before the market shut
// checkedAt: the writer's last vouch, not when the price was observed; statusOf and riskOf give markAt
// a wallet reads free; a contract must be a paid reader of the oracle (the seat)
```

## The times, and what each one bounds

- `markAt` is when the price was observed. `checkedAt` is the last time the writer vouched for the record: its own fresh read was within moveBps of the mark then. They are different clocks: in a calm market an issuer mark can be hours old and vouched for minutes ago.
- `markOf` returns `checkedAt`, not `markAt`. `statusOf` and `riskOf` return both.
- An adapter's `updatedAt` is `checkedAt`, the last vouch, and its `startedAt` is `markAt`. Code that checks `block.timestamp - updatedAt` against a heartbeat bounds how recently the record was vouched for, not how old the price is; the price's age is `block.timestamp - startedAt`.
- Status 7, shut on the held print, carries no limit on the mark's age. Through a weekend or a holiday it is the last print before the shut, however old, and a record that read stale while the market was open reads 7 from the second the market shuts, with no write. Status 8, shut on the weekend mark, holds only within closedMaxAge of its `markAt`.

## Statuses, from least to most priceable

| # | name | meaning |
|---|---|---|
| 0 | unknown | something the feed needs did not answer or is not known, so it is not priced |
| 1 | quiet | the house's writer has not vouched for the record inside its window |
| 2 | halted | the issuer's halt flag is up |
| 3 | paused | the token's pause flag is up, which it raises around a corporate action |
| 4 | rebasing | the multiplier moved, or a split or an unexplained fall was recorded after the mark was observed |
| 5 | disputed | a current Chainlink round contradicts the issuer's mark by more than the dispute line |
| 6 | stale | the market is open and the issuer's quote is old, or a shut-market mark still stands after the reopen |
| 7 | closed-held | the market is closed and the mark is the last print before it shut |
| 8 | closed-marked | the market is closed and the mark is what the token traded at on chain this weekend |
| 9 | extended | a fresh issuer print outside the regular session |
| 10 | live | a fresh issuer print in the regular session, or a token that prints around the clock |

Decided in that order of rules, first match wins: never written; the token's own pause flag up; its multiplier moved; either unread; not vouched inside quietAfter; a halt; a pause; a split or fall since the mark; an unknown verdict, no mark, or a day off the calendar; the market shut; a stale quote; a Chainlink dispute; extended or live. A status of 7 or above never comes with a failed read, a quiet record, a halt, a pause, a moved multiplier, a split since the mark, an unknown verdict, a zero mark or an unknown session.

## Sessions

| # | name |
|---|---|
| 0 | unknown: off the calendar |
| 1 | regular: 09:30 to 16:00 New York, 13:00 on an early close |
| 2 | extended: every other hour from Sunday 20:00 to Friday 20:00 outside a holiday |
| 3 | weekend: Friday 20:00 to Sunday 20:00 |
| 4 | holiday: 20:00 the evening before a full exchange holiday to 20:00 on it (modelled) |
| 5 | continuous: a token that prints around the clock |

## Sources of the mark

| # | name | meaning |
|---|---|---|
| 0 | none | no mark |
| 1 | issuer | the issuer's token mid, dated by the quote |
| 2 | weekend-mark | what the token traded at on chain while the market was shut (the Weekend Mark) |
| 3 | held-print | the last issuer print read with an open verdict before the market shut |

A pool's spot price is never the mark.

## Flags

The writer's flags, each from one desk; none changes the status:

| bit | name | meaning |
|---|---|---|
| 1 | F_DIVIDEND_RECENT | the Dividend Ledger saw a dividend step in the last 7 days |
| 2 | F_SPLIT_RECENT | the Dividend Ledger saw a split-scale step in the last 30 days |
| 4 | F_ANOMALY_RECENT | the Dividend Ledger saw an unexplained fall of the multiplier in the last 30 days |
| 8 | F_ACTION_OPEN | the issuer lists a corporate action on it, not completed and not yet matched to a multiplier step |
| 16 | F_CALENDAR_UNREAD | the issuer's corporate-action calendar did not read, so no open action is not the same as none |
| 32 | F_HALT_RECENT | the writer read the issuer's halt flag up in the last 24 hours |
| 64 | F_TRADED_THROUGH_HALT | Halt Watch saw a hooked pool trade it while the oracle read halted or paused, in the last 24 hours |
| 128 | F_PRINT_FLAT | in an open session the issuer's mid has not changed for longer than the writer allows (30 minutes regular, 4 hours extended) |
| 256 | F_SESSION_MISMATCH | the calendar and the issuer disagree on whether the market is shut |
| 512 | F_LEDGER_LAGGING | the Dividend Ledger is behind the token's own multiplier, or has not read in 25 minutes |
| 1024 | F_WEEKEND_MARK_WITHHELD | a weekend mark exists for the hold but stands on too little, so the held print is carried |
| 2048 | F_CHAIN_GAP | the chain's own pool price and the issuer's print, both fresh, differ by more than 300 bps |
| 4096 | F_ACTION_NO_STEP | a cash distribution's window closed with no multiplier step while the ledger watched |

Read live by the contract in every assessment:

| bit | name | meaning |
|---|---|---|
| 1 | L_PAUSED | the token's own pause flag answered up |
| 2 | L_PAUSE_UNREAD | the token's own pause flag did not answer up or down |
| 4 | L_MULT_MOVED | the token's multiplier differs from the one on record |
| 8 | L_MULT_UNREAD | the token's multiplier did not answer |
| 16 | L_FEED_UNREAD | the Chainlink feed registered for it did not answer a positive, dated round |
| 32 | L_FEED_QUIET | the Chainlink feed's last round is older than the feed allowance |
| 64 | L_FEED_DISAGREES | the Chainlink round and the issuer's mark differ by more than the dispute line |
| 128 | L_UNVOUCHED | the writer's last vouch named this record as one it could not vouch for |
| 256 | L_CALENDAR_ENDED | the on-chain calendar holds no entry for now |
| 512 | L_STEP_AFTER_MARK | the last multiplier step was recorded at or after the time the mark was observed |

## The adapter

`deployAdapter(token, minStatus)` (minStatus 7 to 10) makes an AggregatorV3-shaped adapter at `adapterOf(token, minStatus)`: `latestRoundData()` in 8 decimals returns (round, mark, markAt, checkedAt, round) and reverts NotPriceable below the minimum, so lending code that already reads Chainlink integrates by changing one address. Its startedAt is when the price was observed and its updatedAt is the writer's last vouch, so a heartbeat check on updatedAt alone does not bound the price's age (see the times above). The adapter applies the same seat to its own caller. Anyone may deploy one; it costs its deployer gas.

## Who pays

A read from a wallet or a script (the call's sender is the transaction's origin) is free. A contract reading inside its own transaction must be a paid reader of the HarmonicOracle named as the seat (`0x938097252667b9efac600c8dd1826f54eae96881`): `subscribe` at 100 USDG a month to the operations wallet, the same seat as the oracle's rows and the weekend mark, or build the call with the door's `defi_oracle_subscribe_build`. A read routed through a multicall or an aggregator contract is a read from a contract and is gated like one.

## What it does not do

- It is not advice and sets no lending terms; a status is a measurement of a record at a block.
- The issuer's print and halt flag come from the issuer's HTTP API, relayed by one operations-wallet writer, with no attestation.
- Only 36 tokens have a Chainlink round that can dispute a mark.
- The holiday window is modelled, not read from the issuer, and the calendar ends where the house's holiday table ends, 31 Dec 2027.
- A weekend mark exists for weekends only, not holidays.
- A step's time is when the feed recorded it, within one pass of when it happened.
- A token that does not answer its own pause flag reads unknown.
- It sets no limit on the age of a mark that reads shut on the held print (7), and an adapter's updatedAt is the last vouch, not the time the price was observed.

Stock Tokens are tokenized debt securities issued outside the United States and are not available to US persons; read the issuer's restrictions before you act on any of this.
