102: EMC Perps: show liquidation risk and account health

This commit is contained in:
2026-10-26 06:05:26 +01:00
parent 2f30712004
commit e71a8daa8d
30 changed files with 1070 additions and 48 deletions
+52 -6
View File
@@ -1,10 +1,12 @@
# Phoenix Perps read-only integration
`PhoenixPerpsService`, `PhoenixPerpsAccount`, `PhoenixPerpsPosition` and `PhoenixTwapOrder` live in
`PhoenixPerpsService`, `PhoenixPerpsAccount`, `PhoenixPerpsPosition`, `PhoenixAccountRisk` and `PhoenixTwapOrder` live in
`com.r35157.libs.phoenix.perps`; the REST implementation is in `impl.ref`.
They contain no Evelyn/UI types or signing/trading operations. NenjimHub creates
one adapter without a portfolio cache as `phoenix.perps.default` and injects it into both Evelyn
Perps processes. Each process owns its own wallet list, refresh worker and snapshot.
The adapter also receives the existing `SolanaBlockChain` for local trader PDA
derivation only; risk lookup does not use its RPC or signing operations.
See [Evelyn](Evelyn.md#12-perps-service-og-mission-control) for consumer behavior and
[the canonical system overview](../../r35157_docs/projects/evelyn/README.md#evelynperpsservice)
for configuration and deployment status.
@@ -31,10 +33,17 @@ The implementation uses these official endpoints at `https://perp-api.phoenix.tr
4. [`GET /v1/trader/{authority}/twap?traderPdaIndex=0`](https://github.com/Ellipsis-Labs/rise-public/blob/master/ts/src/api/traders/client.ts)
supplies active TWAP snapshots when the wallet has held positions. Only filled orders
with remaining children on matching existing positions are returned; see [TWAP observations](#twap-observations).
5. [`GET /v1/view/trader/{pubkey}`](https://docs.phoenix.trade/api/trader/get-trader)
supplies risk for each visible account, including funded empty Cross accounts.
Its key is the derived trader account, not the wallet or a TWAP key. See
[venue-reported account risk](#venue-reported-account-risk).
An empty trader state needs only the first request. Otherwise metadata is fetched once
An empty trader state with no visible funded account needs only the first request.
A funded empty Cross account additionally needs its risk read, without market/TWAP reads.
Otherwise metadata is fetched once
and each active symbol's price once per wallet lookup, including when that symbol is
held in several subaccounts, followed by one TWAP read. Request starts are spaced at least one second apart across both environments to avoid
held in several subaccounts, followed by one TWAP read and each visible account's
risk read. Request starts are spaced at least one second apart across both environments to avoid
bursts of per-market reads. Each HTTP request has a 10-second timeout and uses the
Hub's existing HTTP client (no redirects by default). On HTTP 429 the adapter respects
`Retry-After` (seconds or HTTP date; 60 seconds if missing/invalid). Further calls
@@ -92,13 +101,49 @@ external data; EMC's three-second polling remains unchanged.
Positions and prices are separate REST reads and can belong to different slots.
This is a periodic current-notional estimate, not an atomic exchange valuation.
A cycle's duration includes all sequential reads and request pacing before the
60-second pause (32 active markets need at least about 34 seconds for their reads). Failed
60-second pause (32 active markets and one visible account need at least about 35 seconds for their reads). Failed
reads can leave old data visible indefinitely. A visible venue shows `(retained)` after
a failed refresh; cell tooltips include the last-success time. Childless venues are hidden,
with failed or pending first-read status available in their wallet's tooltip. Such unknown
scopes cannot contribute fabricated zero totals.
Trader PDA indices other than 0, streaming and position management are out of scope.
## Venue-reported account risk
The [official address derivation](https://github.com/Ellipsis-Labs/rise-public/blob/master/ts/src/pdas.ts)
uses program `EtrnLzgbS7nMMy5fbD42kXiUzGg8XQzJ972Xtk1cjWih` and seeds
`trader`, authority bytes, a one-byte PDA index and a one-byte subaccount index.
The adapter uses the existing local Solana derivation, validates the returned
authority/trader key/PDA/subaccount, and verifies the nonzero position membership
and signed quantities against trader state. A fill during the separate reads can
therefore cause a retained failed observation rather than attach risk to mismatched
position state. Lookup works without any active TWAP.
TraderView returns `riskState`, `riskTier`, `effectiveCollateral`,
`maintenanceMargin` and position `liquidationPrice`. The actual deployed amount
objects contain integer `value` and `decimals`; the reader computes exact decimal
amounts with decimal exponents from -18 through 18 and ignores formatted `ui` strings.
Positive liquidation prices are
retained; `-1`, zero, other nonpositive values and absent prices are unavailable.
They are not zero risk or infinite safety. Optional missing fields and unsupported
future tiers cannot silently become Safe. Malformed amounts, identity mismatches
and rejected HTTP reads fail the complete wallet/venue observation.
Cross risk belongs to the shared account. The reported effective collateral can
include funding, discounted positive PnL and collateral haircuts; it is distinct
from the overview's undiscounted equity and estimated closing Value. Risk therefore
can be available even where the older capital valuation cannot value spot collateral.
The reported thresholds and tier are preserved without inventing a liquidation engine.
See [Phoenix account health](https://docs.phoenix.trade/phoenix/margin-and-risk/account-health)
and [margin math](https://docs.phoenix.trade/phoenix/margin-and-risk/margin-math).
Account risk carries its original response receipt time. Isolated risk combines
that account's reported liquidation price with its already fetched Phoenix mark;
its observation time is the older receipt. Prices and account data are separate
reads and can lag the chain. Existing failures retain original timestamps. The
[Risk presentation](Evelyn.md#risiko-i-perps) defines five-minute expiration,
neutral isolated distance, venue tier colors and group summaries.
## TWAP observations
TWAP data is matched by requested authority, PDA index, subaccount and market `assetId`
@@ -202,8 +247,9 @@ venue's displayed conventions have not been verified; the UI explains this and
shows dollar PnL. Isolated leverage/ROI use actual collateral. Cross position
Value is inapplicable; Cross account Value counts all shared capital once,
including free collateral when its last position closes. Other aggregate ratios
are blank. No account margin-utilization percentage is claimed; orders/risk
adjustments are not included in such a metric.
are blank. Risk separately displays reported maintenance-margin utilization
against effective collateral. It is not total initial-margin utilization, free
buying power or the ratio used for the Leverage column.
Value is an estimate at separately read reference prices, not an executable quote.
Slippage, price impact, swaps, network fees, account rent, future funding and