100: Add Perps collateral, leverage, value, uPnL and gross-exposure totals in EMC

This commit is contained in:
2026-10-24 17:56:20 +02:00
parent 7e36b7ef95
commit 28640f3203
35 changed files with 9110 additions and 180 deletions
@@ -0,0 +1,2 @@
schema: spec-driven
created: 2026-10-24
@@ -0,0 +1,31 @@
# Design
## Context
See proposal.md. The existing service holds only positions and drops first-read failures from the hierarchy. Jupiter's transaction adapter exposes dummy valuation zeros; Phoenix already validates REST trader state and paces requests. Existing local staged/deleted OpenSpec changes are outside this change.
## Goals / Non-Goals
**Goals:** Exact decimal monetary calculations, explicit incomplete/stale scopes, account-owned Cross capital, and a readable first version using existing configured environments.
**Non-Goals:** Account utilization, inferred Phoenix position ROI/leverage, wallet configuration changes, transaction/quote construction, live deployment, or a permanent test suite.
## Decisions
1. Extend immutable service snapshots with nullable metrics, isolated positions, explicit Cross accounts and last-success/read-failure metadata. Aggregate in the service domain, independently of tree expansion. Sum actual capital once per isolated position/Cross account; sum gross exposure and dollar PnL; propagate unavailable components separately per field. Never aggregate ratios. Preserve a never-read venue as unknown instead of silently excluding it.
2. Add a Jupiter overview-only REST interface and implementation, injected independently of the existing transaction services. Validate count, identities, directions, integer micro-USD fields and complete responses. Use one coherent GET per wallet. Size remains the venue's existing USD size, collateral is its recorded post-fee collateral, price PnL uses pnlBeforeFeesUsd, and leverage/percentage are derived from that collateral. Current live reads match on-chain size/collateral; API percentages are percentage points. Do not use API valueUsd until its valuation is trustworthy: observed valueUsd subtracts openFeesUsd from collateral that already matches post-fee on-chain collateral; profit-cap/collateral valuation semantics are also undocumented. Display unavailable Value with this reason rather than correct one fee while guessing the rest.
3. Extend Phoenix's own account/position result, keeping it independent of Evelyn/UI. Subaccount 0 is Cross; other indices are isolated. Read one trader state and one metadata response, then one price per unique held symbol. Convert quote lots at 1e-6 USD. Price PnL is signed quantity times mark plus virtual quote. Funding is the signed unsettled funding amount returned in trader state, added to equity; accumulated/settled funding is not added again. Closing fee estimate uses current notional times market taker fee times the account's REST fee override. Do not subtract settled trading fees again. Nonzero unsupported spot collateral makes collateral/equity/Value unavailable, while independent notional/PnL remain available.
4. Phoenix position initial margin follows SDK integer tick conversion (floor), integer tier interpolation (truncate the complete interpolated leverage), and ceil division to quote lots. Exposure/PnL keep the original precise REST mark, preserving existing notional meaning. Isolated leverage and ROI use collateral; Cross position leverage/ROI remain unavailable, Cross position Value is inapplicable. Cross group leverage is gross exposure divided by undiscounted equity before estimated exit fees.
5. JavaFX cells only format published domain metrics and notes. Reconcile stable IDs at all levels, including Cross accounts; preserve root and row instances, selection and expansion. Give numbers aligned cells and headers/rows explanatory tooltips, neutral rounded zero, and visually distinct group totals.
## Risks / Trade-offs
- Separate Phoenix state/metadata/price observations → describe reference-price estimates and lack of atomic/executable quotes; exclude slippage, swaps, rent and future funding.
- Unverified Jupiter net proceeds → explicit unavailable Value propagates to higher Value totals; do not publish a partial total.
- Unknown Phoenix spot collateral or missing valuation inputs → only affected fields unavailable; malformed position/identity/required price data still fails the whole scope.
- REST failure or cooldown → retain last success with timestamp and failure notice; no immediate retries or overlapping cycles.
- Additional metrics exceed former three-column layout → preview in the existing EMC dimensions and allow horizontal scrolling at useful minimum widths.
## Migration Plan
No runtime configuration or storage migration. Build/verify locally and leave changes uncommitted for review. A later owner-controlled deployment replaces the binary; rollback restores the previous build. Do not restart the live Hub.
@@ -0,0 +1,29 @@
# Proposal
## Why
The owner cannot assess Evelyn's committed capital and risk from direction and size alone. Issue #100 adds a first inspectable, read-only valuation overview for existing wallets, directly supporting safe Evelyn operation.
## What Changes
- Show Position, Direction, Margin, Collateral, Leverage, Exposure, Value and uPnL beneath a visible Perpetual Contracts root.
- **BREAKING**: Extend snapshot and Phoenix result record contracts to represent shared Cross accounts separately from isolated positions, retaining funded empty Cross accounts.
- Publish precise venue valuations and complete gross exposure, dollar uPnL and closing-value totals without counting shared capital twice. Unsupported metrics remain explicitly unavailable.
- Add an overview-only Jupiter REST reader; preserve the existing trading, signing, alarm and entry-price integration.
- Preserve background refresh timing, independent last-success retention, immutable snapshots and stable UI reconciliation; explain retained or incomplete data.
## Capabilities
### New Capabilities
- `jupiter-perps-overview`: Validated read-only Jupiter position metrics, independent of transaction operations.
### Modified Capabilities
- `evelyn-perps-service`: Account-aware immutable metrics and complete, field-specific aggregation with independent refresh status.
- `evelyn-perps-overview`: Eight columns, Cross hierarchy, totals, formatting and definitions.
- `phoenix-perps-service`: Shared balances, exact price PnL, initial margin and account closing-value estimates.
## Impact
Changes are limited to `.tjava` integration/service/presentation code, passive Hub wiring, OpenSpec and applicable project/shared documentation. No new dependencies, configuration system, trading operations, live Hub restart, commits or pushes. Verification uses the normal build and temporary focused/read-only JavaFX harnesses.
@@ -0,0 +1,33 @@
## MODIFIED Requirements
### Requirement: Perps overview displays a collapsible position hierarchy
The Perps tab SHALL display Position, Direction, Margin, Collateral, Leverage, Exposure, Value and uPnL in that order beneath the visible Perpetual Contracts root. Isolated hierarchy SHALL be wallet → venue → position; each Phoenix Cross account SHALL add a Cross group above its positions. Groups SHALL show applicable totals, leave direction/aggregate ratios blank, and be expanded by default.
#### Scenario: Open either environment's Perps tab
- **WHEN** the owner opens Perps in Production or Test
- **THEN** all eight columns, root totals and the account-aware hierarchy are visible
#### Scenario: Collapse and expand groups
- **WHEN** the owner collapses and expands a wallet, venue or Cross account
- **THEN** descendants hide and return without changing totals
### Requirement: Jupiter positions communicate direction and USD size
Positions SHALL show readable Long/Short direction and Isolated/Cross margin text. USD amounts SHALL use grouping and two decimals; leverage SHALL use a multiplication sign. Signed uPnL SHALL be green for positive, red for negative and neutral for rounded zero without negative zero. Existing faint direction backgrounds SHALL remain; groups SHALL have distinct weight and no direction color. Numeric columns SHALL be right-aligned with useful widths and tooltips.
#### Scenario: Small or unavailable PnL
- **WHEN** PnL rounds to zero or is unavailable
- **THEN** zero has no negative sign/color and unavailable values show an em dash with an explanation
#### Scenario: Inspect real positions
- **WHEN** real Jupiter positions are displayed
- **THEN** direction appears as readable text and background color, with formatted gross USD exposure and applicable valuation metrics
## ADDED Requirements
### Requirement: Definitions distinguish owned collateral and margin requirements
Tooltips SHALL explain isolated collateral, exposure-based leverage, collateral-based ROI, estimated net Value and excluded slippage/swaps/rent. Cross position collateral SHALL be identified as initial margin required against shared collateral. Cross position Value SHALL be unavailable; unsupported Cross ROI/leverage SHALL be explained. Cross account leverage SHALL identify undiscounted equity before exit fees as its denominator.
#### Scenario: Inspect a Cross position
- **WHEN** the owner inspects a Cross row and its account group
- **THEN** the position shows a USD margin requirement and the account shows shared capital once
- **AND** no position-only metric is labeled account utilization
@@ -0,0 +1,53 @@
## MODIFIED Requirements
### Requirement: Wallet configuration belongs to service construction
The Hub SHALL supply the six PROD wallet addresses and one TEST wallet address specified in issue #98, a read-only Jupiter overview reader and a read-only Phoenix integration at construction. Each service SHALL retain immutable configuration and own its snapshot. Wallet addresses SHALL be used as stable identities and display labels. EMC SHALL neither own nor pass wallet configuration; reads SHALL remain parameterless. Existing Jupiter trading/alarm services SHALL retain their behavior.
#### Scenario: Separate configured instances
- **WHEN** the Hub constructs and starts both instances
- **THEN** each instance queries only its configured wallets through its injected integrations
- **AND** the implementation does not define the wallet list internally
### Requirement: Position mapping is stable and complete
The service SHALL preserve Jupiter position-account identities and its existing USD position-size basis. Phoenix SHALL preserve PDA/subaccount/symbol identities and absolute current notional. Isolated positions SHALL remain directly under venues; Cross accounts SHALL own their positions and shared balances separately. Wallet and position ordering SHALL be deterministic. No account membership SHALL be inferred from instrument labels.
#### Scenario: Same-market cross and isolated positions
- **WHEN** a Phoenix wallet holds SOL in Cross and an isolated subaccount
- **THEN** both positions retain distinct IDs and their actual account membership
#### Scenario: Changed and unknown positions
- **WHEN** a successful read returns newly opened positions including an unknown token mint
- **THEN** all returned positions appear with stable IDs and exact USD sizes, and the unknown mint is displayed rather than dropped
### Requirement: Background Jupiter refresh
The service SHALL refresh immediately after startup and wait 60 seconds after completed cycles without overlap or dependence on EMC visibility. It SHALL use a dedicated read-only Jupiter overview and the Phoenix account reader. Snapshot reads SHALL make no integration calls. No signing or position-management operation SHALL occur; existing transaction/alarm reads SHALL retain their behavior.
#### Scenario: Background update while EMC is hidden
- **WHEN** EMC is hidden and the next refresh is due
- **THEN** the service refreshes configured wallets and publishes immutable snapshots
- **AND** all UI snapshot reads remain in-memory
## ADDED Requirements
### Requirement: Complete gross and account-aware totals
Exposure and dollar price PnL SHALL sum through all levels. Exposure SHALL be gross, with positive absolute notionals. Capital and closing Value SHALL count isolated positions and shared Cross accounts once. Missing required components SHALL make only the affected total unavailable. Successfully read empty scopes SHALL total zero. Leverage and PnL percentages SHALL NOT be aggregated.
#### Scenario: Opposite directions and shared collateral
- **WHEN** two Cross positions have notionals 100 USD Long and 100 USD Short, shared collateral 80 USD, combined PnL 5 USD and closing fees 1 USD
- **THEN** exposure totals 200 USD, collateral 80 USD and Value 84 USD at account, venue, wallet and root levels
- **AND** collapsing any UI group does not change totals
#### Scenario: First read fails
- **WHEN** one configured venue has never been read successfully
- **THEN** it remains unknown and cannot silently produce complete portfolio totals
### Requirement: Retained observations communicate status
Each venue SHALL carry its last-success time and whether the latest attempt failed. Failure SHALL preserve positions and account balances independently per wallet/venue. A valid funded empty Cross account SHALL remain visible. Retained data SHALL NOT be described as fresh.
#### Scenario: Last Cross position closes
- **WHEN** a successful refresh returns a Cross account with nonzero collateral and no positions
- **THEN** its capital and Value remain included without synthetic positions
#### Scenario: Retained last success
- **WHEN** a subsequent read fails
- **THEN** its previous observations remain available with their original success time and failure indication
@@ -0,0 +1,27 @@
## Purpose
Provide precise read-only Jupiter position metrics for capital and risk inspection without altering transaction or alarm behavior.
## ADDED Requirements
### Requirement: Coherent read-only position overview
The overview SHALL read the official positions response for one wallet, validate completeness and unique stable identities, and return immutable results. USD integer amounts SHALL be converted from micro-USD without binary floating point. Requests SHALL be interruptible and have bounded timeouts; failed or malformed reads SHALL NOT become empty portfolios.
#### Scenario: Incomplete response
- **WHEN** count differs from the returned position count, or identities are duplicated
- **THEN** the read fails and no partial portfolio is published
#### Scenario: Valid empty response
- **WHEN** the response contains count zero and an empty position list
- **THEN** the successful result clears that wallet's Jupiter scope
### Requirement: Verified metrics and unavailable valuations
The overview SHALL preserve size basis, use post-fee USD collateral and price PnL before separate fees, and calculate leverage and ROI against positive collateral. Unverified closing valuations SHALL be unavailable with an explanation; placeholder zeros and double subtraction of settled fees SHALL NOT be used.
#### Scenario: Unverified API closing value
- **WHEN** API value subtracts opening fees already settled in collateral or does not establish profit-cap valuation
- **THEN** closing Value is unavailable, while independently verified metrics remain available
#### Scenario: Nonpositive collateral
- **WHEN** a position's collateral is zero or negative
- **THEN** its leverage and PnL percentage are unavailable
@@ -0,0 +1,20 @@
## ADDED Requirements
### Requirement: Separate account capital and precise position metrics
The reader SHALL return immutable account balances separately from positions, including funded empty Cross accounts. Quote lots SHALL convert at 1e-6 USD. Price PnL SHALL use precise virtual quote and signed base amounts at mark. Initial position margin SHALL follow size-dependent tiers including venue interpolation and rounding. Unsupported non-USDC collateral SHALL make affected valuations unavailable instead of being ignored.
#### Scenario: Size between margin tiers
- **WHEN** a position lies between two leverage tiers
- **THEN** its initial margin follows interpolated integer leverage and upward quote-lot rounding, rather than the market's maximum leverage
#### Scenario: Unsupported spot collateral
- **WHEN** an account has nonzero spot collateral that cannot be valued reliably
- **THEN** collateral, equity and closing Value are unavailable while independent exposure and price PnL remain available
### Requirement: Estimated account and isolated closing proceeds
Equity SHALL include collateral, undiscounted price PnL and signed unsettled funding once. Estimated Value SHALL subtract estimated closing taker fees with account fee overrides. Settled fees/funding SHALL NOT be subtracted again. Isolated position leverage/ROI SHALL use actual collateral; Cross position leverage/ROI SHALL remain unavailable unless their venue convention is verified.
#### Scenario: Funding signs and fee override
- **WHEN** collateral is 100 USD, PnL 5 USD, unsettled funding -2 USD, and estimated exit fee 1 USD
- **THEN** equity is 103 USD and estimated Value 102 USD
- **AND** effective Cross leverage uses 103 USD as its denominator
@@ -0,0 +1,22 @@
# Tasks
## 1. Snapshot and totals
- [x] 1.1 Add immutable metrics, Cross accounts and read status; verify gross totals, missing components and no shared-capital double counting with a temporary focused harness.
- [x] 1.2 Update the service contract documentation and verify it distinguishes unknown, empty and retained observations.
## 2. Venue valuations and refresh
- [x] 2.1 Add and wire an overview-only Jupiter REST reader; verify live units, completeness, unsupported Value, positive/negative PnL and zero denominators with read-only data and temporary fixtures; document valuation definitions.
- [x] 2.2 Extend Phoenix account reads and exact valuation/margin rules; verify tiers, funding signs, fee overrides, unknown collateral and funded empty accounts with temporary fixtures and live data; update Phoenix documentation.
- [x] 2.3 Publish per-wallet/venue immutable refresh results and status; verify failure retention, recovery to empty, independent attempts and parameterless no-I/O reads with a temporary harness; update shared Evelyn documentation.
## 3. Presentation
- [x] 3.1 Implement eight columns, root/Cross hierarchy, signed formatting and tooltips; verify a standalone JavaFX preview with real read-only data plus controlled edge cases, both environments and preserved tree state.
- [x] 3.2 Document UI definitions and unavailable metrics; verify documentation agrees with rendered behavior.
## 4. Integrated verification
- [x] 4.1 Run the normal Gradle build and applicable existing checks, validate OpenSpec strictly, and record observation times, preview evidence and remaining valuation limitations in verification.md.
- [x] 4.2 Inspect the final diffs for unintended changes and confirm no commits, pushes, shared Detag edits or live Hub restart occurred.
@@ -0,0 +1,117 @@
# Verification — issue #100
Verified locally on 24 October 2026; no live Hub was started or restarted, and no
signing, trading, activation, deposits or withdrawals were performed. Changes are
uncommitted and unpushed. Existing staged/deleted change-97/change-99 artifacts
and the pre-existing untracked NFS log file were left untouched.
## Build and focused checks
- `JAVA_HOME=/usr/local/software/java/jfx-25 ./gradlew build` passed against the
final sources: 42 existing tests, zero failures/errors. Some intermediate runs
encountered transient NFS locks while deleting binary test results; normal
reruns succeeded without changing build configuration.
- Temporary `/tmp/issue100-check/Issue100Check.java` passed 66 focused checks.
No permanent test suite was added. Fixtures exercised gross Long/Short totals,
once-only Cross capital/Value, missing components, valid empty scopes, immutable
collections, positive/negative PnL, zero/negative denominators, Jupiter response
completeness/duplicates/units, Phoenix funding signs and fee overrides,
size-dependent tiers and quote-lot rounding, negative base-lot exponents,
unsupported spot collateral, funded empty Cross accounts, failed refresh,
retained timestamps, recovery to empty, independent wallet/venue attempts,
no-I/O snapshot access and Phoenix 429 cooldown.
- Hand-checkable Cross fixture: 100 USD Long + 100 USD Short = 200 USD gross
exposure; shared collateral 80, price PnL +5, net unsettled funding 0 and
exit fees 1 give equity 85 and Value 84. Cross leverage is 200/85. Initial
margin requirements of 10 per child are not added to the 80 owned capital.
- Tier fixture: 150 base lots at exponent 2 and price 100.009, tick size 100,
between tiers (100 lots, 10×) and (200 lots, 5×), uses integer leverage 7
and initial margin 21.428572 USD. Exposure remains 150.0135 USD.
## Read-only live comparison
The new adapters were read directly, outside the Hub, from
**2026-10-24 10:18:38 UTC to 10:19:13 UTC** (12:18–12:19 CEST).
The controlled PROD wallet was `H5Yrdm7B4FHhTxhPw9SVwFzQyTEL6bAPptZuTyRxcepq`;
the existing TEST wallet was `6NyYEoXmJyBXvU4pvv9aoxZNdyqqtkmPBZFEDkRDHcqp`.
The other five PROD wallets remain configured, but were not all read for this
preview. Counts and prices are observations, not fixed expectations.
| Observed scope | Positions | Collateral USD | Gross Exposure USD | Price uPnL USD | Estimated Value USD |
| --- | ---: | ---: | ---: | ---: | ---: |
| Controlled PROD wallet, Phoenix | 32 | 938.131075 | 4527.060948 | -328.677770 | 608.027266 |
| Controlled PROD wallet, Jupiter | 6 | 54.993558 | 704.568047 | -15.752428 | unavailable |
| Controlled PROD wallet, combined | 38 | 993.124633 | 5231.628995 | -344.430198 | unavailable |
| TEST wallet, both venues | 0 | 0 | 0 | 0 | 0 |
Jupiter API units and collateral were independently checked against a read-only
Solana `getMultipleAccounts` response for all six returned position identities.
API `sizeUsd` and `collateralUsd` matched the on-chain values exactly. USD and
price fields use six decimal places; percentage fields are already percentage
points. The UI's leverage and isolated PnL percentage are derived independently
from precise collateral, rather than using rounded API ratios.
Phoenix uses exact signed quantity × REST mark plus virtual quote for price
PnL; quote lots are 1e-6 USD. Estimated equity adds signed unsettled funding,
and Value subtracts estimated closing taker fees with the REST account override.
Settled funding/fees are not charged again. Tier interpolation and integer price
rounding were cross-checked against the official SDK `margin/math.ts`,
`margin/compute.ts`, `units.ts` and Rust leverage-tier implementation.
## JavaFX verification
A standalone `/tmp/issue100-check/PerpsPreview.java` used the actual immutable
read-only PROD/TEST observations above. Controlled edge cases were a separate
preview tab and are absent from application sources. It checked eight columns,
the visible root, Cross grouping, preserved selection/expansion/row identity on
unchanged and changed snapshots, positive/negative/rounded-zero PnL and tooltip
definitions. Both 1100-pixel and 600-pixel layouts were inspected; the latter
has horizontal scrolling at useful column widths. Root labels use explicit readable text color, including after selection and tab
changes; column factories are installed before the initial root.
Screenshots produced and visually inspected:
- `/tmp/issue100-check/preview-prod.png`
- `/tmp/issue100-check/preview-test.png`
- `/tmp/issue100-check/preview-edges.png`
- `/tmp/issue100-check/preview-tooltip.png`
- `/tmp/issue100-check/preview-small-window.png`
## OpenSpec and documentation
The change and all four affected main specifications pass strict validation:
```text
openspec validate 100-perps-capital-and-risk --strict
openspec validate evelyn-perps-service --type spec --strict
openspec validate evelyn-perps-overview --type spec --strict
openspec validate phoenix-perps-service --type spec --strict
openspec validate jupiter-perps-overview --type spec --strict
```
Main specs were synchronized; the change remains active for review. Project
Evelyn/Phoenix documentation and the shared `r35157_docs/projects/evelyn/README.md`
were updated. `git diff --check` passed. Whole-project `openspec validate --all
--strict` reports 24 passed and two pre-existing failures: placeholder Purpose
sections in `javafx-runtime-service` and `nenjim-process-manager`. Those unrelated
specifications were not changed.
## Known unavailable metrics and valuation limits
- **Jupiter Value is unavailable**, including higher totals requiring it. The
observed API `valueUsd` subtracts opening fees from collateral that already
matches post-fee on-chain collateral. Its collateral-valuation/profit-cap
treatment is not verified. No fabricated zero, partial total or speculative
fee-only correction is displayed; see `docs/JupiterPerpsOverview.md`.
- Phoenix Cross position leverage/ROI conventions remain unverified. Those
ratios are unavailable; dollar PnL is shown and account effective leverage
is provided. Individual Cross position Value is inapplicable.
- Nonzero unsupported spot collateral or missing funding/fees/PnL makes the
affected collateral/equity/Value unavailable. This version does not claim full
account utilization or include open-order risk adjustments in such a metric.
- Valuations use periodic reference observations, not executable prices.
Slippage, price impact, swaps, network fees, account rent and future funding
are excluded. Phoenix state and prices are separate REST observations.
Failed refreshes can retain old data indefinitely with explicit status/time.
- No changes were made to shared `detag.conf`, wallet lists, trading/alarm
implementations, EMC polling cadence or the 60-second post-cycle delay.
@@ -0,0 +1,2 @@
schema: spec-driven
created: 2026-10-24
@@ -0,0 +1,22 @@
# Design
## Context
See proposal.md. Venue snapshots include successful-empty, not-yet-read and failed scopes. Cross accounts hold capital independently of positions, so position count alone cannot determine visibility.
## Goals / Non-Goals
**Goals:** Remove childless headings while preserving capital totals and failure visibility.
**Non-Goals:** Changing integration reads, snapshot data, wallet visibility or refresh scheduling.
## Decisions
- Skip a venue during tree reconciliation only when both its positions and Cross-account lists are empty. Filtering the service snapshot instead would lose unknown scopes and could produce misleading totals.
- Include childless venues' read status in wallet tooltip notes so hiding their headings does not hide the reason for unavailable totals. Keep the existing venue tooltips when rows exist.
- Reconcile surviving items with the existing IDs; a venue returning after removal is a new expanded group. Its wallet and other venue rows remain retained.
## Risks / Trade-offs
- Hidden failed/initial reads can resemble a successfully empty portfolio → preserve unavailable totals and expose the exact status on the wallet row.
- A Phoenix account may have capital without positions → treat the Cross account itself as a child.
@@ -0,0 +1,26 @@
# Proposal
## Why
Empty Jupiter and Phoenix headings add clutter to Evelyn's Perps capital/risk overview. The owner wants a venue shown only when it has something beneath it.
## What Changes
- Hide venue rows with neither isolated positions nor Cross accounts; show them again when data supplies children.
- Retain Phoenix groups containing funded Cross accounts without open positions.
- Keep hidden scopes in the service snapshot and totals; expose their read status through wallet tooltips.
- Replace documentation that says empty venue headings remain visible.
## Capabilities
### New Capabilities
None.
### Modified Capabilities
- `evelyn-perps-overview`: Suppress childless venues without discarding valuation or read-status information.
## Impact
Local `.tjava` presentation code, the overview specification, and relevant project/shared documentation only. No integration, refresh, wallet configuration, signing or trading changes. No commits, pushes or live Hub restart.
@@ -0,0 +1,23 @@
## ADDED Requirements
### Requirement: Venue visibility follows available child rows
EMC SHALL hide venue rows with neither positions nor Cross accounts and show them when children reappear. Funded empty Cross accounts SHALL keep their venue visible. Hidden venues SHALL remain in snapshots and totals, with read status available in wallet tooltips. Unknown scopes SHALL NOT become zero totals; failed reads SHALL preserve last-success totals. Wallet rows and the root SHALL remain visible.
#### Scenario: Empty venue
- **WHEN** Jupiter or Phoenix has no positions and no Cross accounts
- **THEN** its heading is absent from that wallet's displayed children
- **AND** a successful empty scope still contributes zero to totals
#### Scenario: Funded Cross account without positions
- **WHEN** Phoenix returns a Cross account with capital but no open positions
- **THEN** Phoenix and its Cross account remain visible with that capital counted once
#### Scenario: Positions disappear and return
- **WHEN** a venue loses its last child and later returns a position
- **THEN** its heading disappears and reappears with the correct child
- **AND** unrelated retained rows keep their selection and expansion state
#### Scenario: Hidden scope has not been read successfully
- **WHEN** a childless venue has never been read successfully and its first read is pending or failed
- **THEN** the heading is hidden and its read status is available on the wallet tooltip
- **AND** required totals remain unavailable rather than reporting zero
@@ -0,0 +1,11 @@
# Tasks
## 1. Presentation and documentation
- [x] 1.1 Filter childless venues and retain their wallet-level read status; verify empty, funded Cross, unknown/failed and returning-position cases in the standalone preview while preserving unrelated tree state.
- [x] 1.2 Update the overview specification and relevant project/shared documentation; verify obsolete visible-empty-group claims are removed and links remain valid.
## 2. Integrated verification
- [x] 2.1 Run the normal Gradle build and strict validation for the change and affected spec; record the preview/build results.
- [x] 2.2 Inspect the final diffs and confirm existing unrelated changes, integration behavior and the no-commit/no-push constraint are preserved.
@@ -0,0 +1,24 @@
# Verification
Verified locally on 24 October 2026. No commits, pushes or live Hub restart.
- Normal `JAVA_HOME=/usr/local/software/java/jfx-25 ./gradlew build` passed:
42 existing tests, zero failures.
- Strict OpenSpec validation passed for this change and `evelyn-perps-overview`;
the added requirement is synchronized into the main specification.
- Reused the temporary standalone JavaFX preview outside application sources.
Checked both empty venues hidden with wallet retained; pending/failed hidden
scopes still unavailable in totals with wallet-level status; funded empty
Cross account keeping Phoenix visible; Jupiter appearing, disappearing and
returning; unrelated Phoenix item identity, selection and collapse retained;
a previously successful empty read still contributing zero after a failure.
- Existing preview checks for eight columns, formatting and tree reconciliation
also passed. Screenshots are under `/tmp/issue100-check/hide-empty-venues/`;
the TEST and controlled-case views were visually inspected. The preview reused
prior read-only snapshots, with no new exchange reads or application sample data.
- Updated `docs/Evelyn.md`, `docs/PhoenixPerps.md` and the canonical shared
`r35157_docs/projects/evelyn/README.md`, replacing obsolete claims about visible
empty groups and their error/loading indicators. Local links and diff checks passed.
- The source change is confined to `PerpsTab.tjava`; snapshots, integrations,
totals and refresh timing are unchanged. Existing unrelated staged/deleted
OpenSpec artifacts were left untouched.
+40 -7
View File
@@ -7,16 +7,15 @@ Let the owner inspect Evelyn perpetual positions grouped by wallet and venue in
## Requirements
### Requirement: Perps overview displays a collapsible position hierarchy
The Perps tab SHALL display a tree table with columns `Position`, `Direction`, and `Position size, USD`. The hierarchy SHALL be wallet → venue → position, expanded by default, with collapsible wallet and venue groups. Wallet and venue rows SHALL leave direction and size empty.
The Perps tab SHALL display Position, Direction, Margin, Collateral, Leverage, Exposure, Value and uPnL in that order beneath the visible Perpetual Contracts root. Isolated hierarchy SHALL be wallet → venue → position; each Phoenix Cross account SHALL add a Cross group above its positions. Groups SHALL show applicable totals, leave direction/aggregate ratios blank, and be expanded by default.
#### Scenario: Open either environment's Perps tab
- **WHEN** the owner opens Perps in Production or Test
- **THEN** the three named columns and the fully expanded hierarchy are visible
- **AND** direction and size appear only on position rows
- **THEN** all eight columns, root totals and the account-aware hierarchy are visible
#### Scenario: Collapse and expand groups
- **WHEN** the owner collapses and then expands a wallet or venue
- **THEN** its descendants are hidden and then shown again
- **WHEN** the owner collapses and expands a wallet, venue or Cross account
- **THEN** descendants hide and return without changing totals
### Requirement: Asynchronous polling follows window visibility
EMC SHALL request the current Perps snapshot from each environment's own service immediately on opening and every three seconds while visible, without blocking the UI thread. UI updates SHALL occur only on the JavaFX thread. Hiding the window SHALL stop polling; reopening SHALL create only one active polling loop and ignore results belonging to an earlier window session. Poll failures SHALL preserve the current view and allow subsequent attempts.
@@ -43,11 +42,15 @@ The Perps table SHALL retain matching wallet, venue and position identities acro
- **THEN** the same rows remain, with the same expansion and selection and no rebuild or refresh
### Requirement: Jupiter positions communicate direction and USD size
Position rows SHALL show Long or Short and USD sizes with grouping separators and two decimal places. Long rows SHALL have faint green backgrounds and Short rows faint red backgrounds; wallet and venue rows SHALL have no direction-based color. Existing columns and hierarchy SHALL be preserved.
Positions SHALL show readable Long/Short direction and Isolated/Cross margin text. USD amounts SHALL use grouping and two decimals; leverage SHALL use a multiplication sign. Signed uPnL SHALL be green for positive, red for negative and neutral for rounded zero without negative zero. Existing faint direction backgrounds SHALL remain; groups SHALL have distinct weight and no direction color. Numeric columns SHALL be right-aligned with useful widths and tooltips.
#### Scenario: Small or unavailable PnL
- **WHEN** PnL rounds to zero or is unavailable
- **THEN** zero has no negative sign/color and unavailable values show an em dash with an explanation
#### Scenario: Inspect real positions
- **WHEN** real Jupiter positions are displayed
- **THEN** direction appears as readable text and background color, with formatted USD size
- **THEN** direction appears as readable text and background color, with formatted gross USD exposure and applicable valuation metrics
### Requirement: EMC presents service data without sample notices
EMC SHALL obtain positions only from its environment's in-memory service snapshot and SHALL contain no sample positions, sample notices, illustrative-Phoenix notices, or Refresh button. EMC SHALL own UI components, colors and formatting; integration reads SHALL belong to the service.
@@ -56,3 +59,33 @@ EMC SHALL obtain positions only from its environment's in-memory service snapsho
- **WHEN** positions are added or removed after refresh
- **THEN** EMC reconciles rows by identity while preserving retained selections and expansion state
- **AND** no sample labels or Phoenix examples appear
### Requirement: Definitions distinguish owned collateral and margin requirements
Tooltips SHALL explain isolated collateral, exposure-based leverage, collateral-based ROI, estimated net Value and excluded slippage/swaps/rent. Cross position collateral SHALL be identified as initial margin required against shared collateral. Cross position Value SHALL be unavailable; unsupported Cross ROI/leverage SHALL be explained. Cross account leverage SHALL identify undiscounted equity before exit fees as its denominator.
#### Scenario: Inspect a Cross position
- **WHEN** the owner inspects a Cross row and its account group
- **THEN** the position shows a USD margin requirement and the account shows shared capital once
- **AND** no position-only metric is labeled account utilization
### Requirement: Venue visibility follows available child rows
EMC SHALL hide venue rows with neither positions nor Cross accounts and show them when children reappear. Funded empty Cross accounts SHALL keep their venue visible. Hidden venues SHALL remain in snapshots and totals, with read status available in wallet tooltips. Unknown scopes SHALL NOT become zero totals; failed reads SHALL preserve last-success totals. Wallet rows and the root SHALL remain visible.
#### Scenario: Empty venue
- **WHEN** Jupiter or Phoenix has no positions and no Cross accounts
- **THEN** its heading is absent from that wallet's displayed children
- **AND** a successful empty scope still contributes zero to totals
#### Scenario: Funded Cross account without positions
- **WHEN** Phoenix returns a Cross account with capital but no open positions
- **THEN** Phoenix and its Cross account remain visible with that capital counted once
#### Scenario: Positions disappear and return
- **WHEN** a venue loses its last child and later returns a position
- **THEN** its heading disappears and reappears with the correct child
- **AND** unrelated retained rows keep their selection and expansion state
#### Scenario: Hidden scope has not been read successfully
- **WHEN** a childless venue has never been read successfully and its first read is pending or failed
- **THEN** the heading is hidden and its read status is available on the wallet tooltip
- **AND** required totals remain unavailable rather than reporting zero
+32 -5
View File
@@ -22,7 +22,7 @@ The service SHALL run as two separate Hub-managed processes registered as `evely
- **AND** no placeholder positions are generated before successful retrieval
### Requirement: Wallet configuration belongs to service construction
The Hub SHALL supply the six PROD wallet addresses and one TEST wallet address specified in issue #98 and the corresponding existing Jupiter integration plus a read-only Phoenix integration at construction. Each service SHALL retain immutable configuration and own its snapshot. Wallet addresses SHALL be used as stable identities and display labels. EMC SHALL neither own nor pass wallet configuration; reads SHALL remain parameterless.
The Hub SHALL supply the six PROD wallet addresses and one TEST wallet address specified in issue #98, a read-only Jupiter overview reader and a read-only Phoenix integration at construction. Each service SHALL retain immutable configuration and own its snapshot. Wallet addresses SHALL be used as stable identities and display labels. EMC SHALL neither own nor pass wallet configuration; reads SHALL remain parameterless. Existing Jupiter trading/alarm services SHALL retain their behavior.
#### Scenario: Separate configured instances
- **WHEN** the Hub constructs and starts both instances
@@ -30,15 +30,19 @@ The Hub SHALL supply the six PROD wallet addresses and one TEST wallet address s
- **AND** the implementation does not define the wallet list internally
### Requirement: Background Jupiter refresh
The service SHALL refresh immediately after startup and wait 60 seconds between completed cycles, without overlapping cycles or depending on EMC visibility. It SHALL use Jupiter and Phoenix read operations and respect the existing Jupiter cache. Snapshot reads SHALL make no integration calls. No transaction signing or position-management operation SHALL occur.
The service SHALL refresh immediately after startup and wait 60 seconds after completed cycles without overlap or dependence on EMC visibility. It SHALL use a dedicated read-only Jupiter overview and the Phoenix account reader. Snapshot reads SHALL make no integration calls. No signing or position-management operation SHALL occur; existing transaction/alarm reads SHALL retain their behavior.
#### Scenario: Background update while EMC is hidden
- **WHEN** EMC is hidden and the next refresh is due
- **THEN** the service still refreshes configured wallets and publishes immutable snapshots
- **AND** no freshness guarantee bypasses the existing 60-second integration cache
- **THEN** the service refreshes configured wallets and publishes immutable snapshots
- **AND** all UI snapshot reads remain in-memory
### Requirement: Position mapping is stable and complete
The service SHALL map position account to stable ID, mint to known instrument label or raw mint fallback, direction to Long/Short, and positionSize to USD size. Wallet order SHALL follow configuration, and positions SHALL be ordered deterministically by position account. Phoenix SHALL use PDA index, subaccount index and symbol as stable identity, signed quantity for direction and absolute current USD notional for size. Jupiter and Phoenix SHALL appear as separate venues, with no subaccount tree level.
The service SHALL preserve Jupiter position-account identities and its existing USD position-size basis. Phoenix SHALL preserve PDA/subaccount/symbol identities and absolute current notional. Isolated positions SHALL remain directly under venues; Cross accounts SHALL own their positions and shared balances separately. Wallet and position ordering SHALL be deterministic. No account membership SHALL be inferred from instrument labels.
#### Scenario: Same-market cross and isolated positions
- **WHEN** a Phoenix wallet holds SOL in Cross and an isolated subaccount
- **THEN** both positions retain distinct IDs and their actual account membership
#### Scenario: Changed and unknown positions
- **WHEN** a successful read returns newly opened positions including an unknown token mint
@@ -56,3 +60,26 @@ A failed read or mapping SHALL retain the last successful data for that wallet a
- **WHEN** Phoenix fails for a wallet while Jupiter succeeds, or vice versa
- **THEN** only the successful venue is updated and the failed venue retains its previous data
- **AND** subsequent wallets are still attempted
### Requirement: Complete gross and account-aware totals
Exposure and dollar price PnL SHALL sum through all levels. Exposure SHALL be gross, with positive absolute notionals. Capital and closing Value SHALL count isolated positions and shared Cross accounts once. Missing required components SHALL make only the affected total unavailable. Successfully read empty scopes SHALL total zero. Leverage and PnL percentages SHALL NOT be aggregated.
#### Scenario: Opposite directions and shared collateral
- **WHEN** two Cross positions have notionals 100 USD Long and 100 USD Short, shared collateral 80 USD, combined PnL 5 USD and closing fees 1 USD
- **THEN** exposure totals 200 USD, collateral 80 USD and Value 84 USD at account, venue, wallet and root levels
- **AND** collapsing any UI group does not change totals
#### Scenario: First read fails
- **WHEN** one configured venue has never been read successfully
- **THEN** it remains unknown and cannot silently produce complete portfolio totals
### Requirement: Retained observations communicate status
Each venue SHALL carry its last-success time and whether the latest attempt failed. Failure SHALL preserve positions and account balances independently per wallet/venue. A valid funded empty Cross account SHALL remain visible. Retained data SHALL NOT be described as fresh.
#### Scenario: Last Cross position closes
- **WHEN** a successful refresh returns a Cross account with nonzero collateral and no positions
- **THEN** its capital and Value remain included without synthetic positions
#### Scenario: Retained last success
- **WHEN** a subsequent read fails
- **THEN** its previous observations remain available with their original success time and failure indication
@@ -0,0 +1,29 @@
# jupiter-perps-overview Specification
## Purpose
Provide precise read-only Jupiter position metrics for capital and risk inspection without altering transaction or alarm behavior.
## Requirements
### Requirement: Coherent read-only position overview
The overview SHALL read the official positions response for one wallet, validate completeness and unique stable identities, and return immutable results. USD integer amounts SHALL be converted from micro-USD without binary floating point. Requests SHALL be interruptible and have bounded timeouts; failed or malformed reads SHALL NOT become empty portfolios.
#### Scenario: Incomplete response
- **WHEN** count differs from the returned position count, or identities are duplicated
- **THEN** the read fails and no partial portfolio is published
#### Scenario: Valid empty response
- **WHEN** the response contains count zero and an empty position list
- **THEN** the successful result clears that wallet's Jupiter scope
### Requirement: Verified metrics and unavailable valuations
The overview SHALL preserve size basis, use post-fee USD collateral and price PnL before separate fees, and calculate leverage and ROI against positive collateral. Unverified closing valuations SHALL be unavailable with an explanation; placeholder zeros and double subtraction of settled fees SHALL NOT be used.
#### Scenario: Unverified API closing value
- **WHEN** API value subtracts opening fees already settled in collateral or does not establish profit-cap valuation
- **THEN** closing Value is unavailable, while independently verified metrics remain available
#### Scenario: Nonpositive collateral
- **WHEN** a position's collateral is zero or negative
- **THEN** its leverage and PnL percentage are unavailable
@@ -36,3 +36,22 @@ HTTP, authentication, malformed schema, identity mismatch and numeric conversion
- **THEN** request starts SHALL be spaced at least one second apart
- **AND** HTTP 429 SHALL fail the lookup and establish a shared cooldown from Retry-After (seconds or HTTP date, 60 seconds fallback)
- **AND** lookups during cooldown SHALL fail without external calls, with no immediate retry
### Requirement: Separate account capital and precise position metrics
The reader SHALL return immutable account balances separately from positions, including funded empty Cross accounts. Quote lots SHALL convert at 1e-6 USD. Price PnL SHALL use precise virtual quote and signed base amounts at mark. Initial position margin SHALL follow size-dependent tiers including venue interpolation and rounding. Unsupported non-USDC collateral SHALL make affected valuations unavailable instead of being ignored.
#### Scenario: Size between margin tiers
- **WHEN** a position lies between two leverage tiers
- **THEN** its initial margin follows interpolated integer leverage and upward quote-lot rounding, rather than the market's maximum leverage
#### Scenario: Unsupported spot collateral
- **WHEN** an account has nonzero spot collateral that cannot be valued reliably
- **THEN** collateral, equity and closing Value are unavailable while independent exposure and price PnL remain available
### Requirement: Estimated account and isolated closing proceeds
Equity SHALL include collateral, undiscounted price PnL and signed unsettled funding once. Estimated Value SHALL subtract estimated closing taker fees with account fee overrides. Settled fees/funding SHALL NOT be subtracted again. Isolated position leverage/ROI SHALL use actual collateral; Cross position leverage/ROI SHALL remain unavailable unless their venue convention is verified.
#### Scenario: Funding signs and fee override
- **WHEN** collateral is 100 USD, PnL 5 USD, unsettled funding -2 USD, and estimated exit fee 1 USD
- **THEN** equity is 103 USD and estimated Value 102 USD
- **AND** effective Cross leverage uses 103 USD as its denominator