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

This commit is contained in:
2026-10-24 17:53:30 +02:00
parent e0b69f6430
commit 828f01db7c
10 changed files with 154 additions and 9 deletions
+6 -2
View File
@@ -461,7 +461,10 @@ EMC viser **Position | Direction | Margin | Collateral | Leverage | Exposure | V
under den synlige rod **Perpetual Contracts**. Isolerede positioner har wallet → venue →
position; Phoenix Cross har wallet → venue → Cross → position. Positionens konto-ID
bestemmer identitet og tilhørsforhold, aldrig instrumentteksten. Wallets følger den
hidtidige konfiguration med Jupiter før Phoenix. Eksisterende foldestatus, markering og
hidtidige konfiguration med Jupiter før Phoenix. En venue uden positioner eller Cross-konti
skjules og vises igen, når der kommer indhold. Skjulte venues indgår fortsat i snapshot
og totaler; deres læsestatus findes på wallet-rækkens tooltip. En Cross-konto med kapital
holder derfor Phoenix synlig, selv uden åbne positioner. Eksisterende foldestatus, markering og
rækkeobjekter bevares; uændrede snapshots opdaterer ikke træet. Vinduesstart er 1100 × 600,
og smallere vinduer kan scrolle vandret, når kolonnernes minimumsbredder kræver det.
@@ -493,7 +496,8 @@ Beløb har tusindtalsseparator og to decimaler, leverage × og pris-uPnL et synl
Positiv uPnL er grøn, negativ rød, afrundet nul neutral uden negativt nul. De svage
Long/Short-baggrunde bevares; grupper har tydeligere skrift og ingen retningsfarve.
Tooltips forklarer definitioner, manglende værdier og sidste succesfulde læsetidspunkt.
Ved fejl markeres venue som retained eller unavailable; gamle tal beskrives ikke som nye.
Efter fejl markeres en synlig venue som retained; læsestatus for skjulte venues findes
på wallet-rækkens tooltip. Gamle tal beskrives ikke som nye.
EMC spørger hver miljøservice straks ved åbning og hvert tredje sekund uden at blokere
JavaFX-tråden. Skjul/luk stopper kun EMC-polling. Der er ingen sample-data, Refresh-knap,
+7 -5
View File
@@ -79,7 +79,9 @@ Each Evelyn process performs an initial background refresh, then waits 60 second
following each completed cycle. Jupiter and Phoenix are attempted independently for
each configured wallet. Successful results replace only that wallet/venue; failures
log wallet and venue and retain its last successful result. A valid empty result
replaces that venue's positions and account balances, preserving its group.
replaces that venue's positions and account balances. EMC hides the venue heading
when it has neither position rows nor Cross accounts; its read status remains in
the wallet tooltip and the scope still contributes to snapshot totals.
Funded Cross accounts remain visible when they no longer hold positions. Snapshot reads never fetch
external data; EMC's three-second polling remains unchanged.
@@ -87,10 +89,10 @@ 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 33 seconds for their reads). Failed
reads can leave old data visible indefinitely. The venue now shows `(retained)` after
a failed refresh, or `(unavailable)` before any successful read; cell tooltips include
the last-success time. Initial not-yet-read scopes show `(loading)` and cannot contribute
fabricated zero totals.
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.
@@ -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.
@@ -67,3 +67,25 @@ Tooltips SHALL explain isolated collateral, exposure-based leverage, collateral-
- **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
@@ -162,11 +162,20 @@ final class PerpsTab {
List<TreeItem<PositionRow>> wallets = new ArrayList<>();
Map<String, TreeItem<PositionRow>> existingWallets = childrenById(table.getRoot());
for (PerpsSnapshot.Wallet wallet : snapshot.wallets()) {
String walletNotes = TOTAL_NOTES;
for (PerpsSnapshot.Venue venue : wallet.venues()) {
if (venue.positions().isEmpty() && venue.crossAccounts().isEmpty()) {
walletNotes += "\n" + venue.name() + " (no rows): " + status(venue);
}
}
TreeItem<PositionRow> walletItem = item(existingWallets,
group(wallet.id(), wallet.name(), wallet.totals(), TOTAL_NOTES));
group(wallet.id(), wallet.name(), wallet.totals(), walletNotes));
List<TreeItem<PositionRow>> venues = new ArrayList<>();
Map<String, TreeItem<PositionRow>> existingVenues = childrenById(walletItem);
for (PerpsSnapshot.Venue venue : wallet.venues()) {
if (venue.positions().isEmpty() && venue.crossAccounts().isEmpty()) {
continue;
}
String status = status(venue);
String label = venue.name() + (venue.lastSuccess() == null
? venue.failed() ? " (unavailable)" : " (loading)" : venue.failed() ? " (retained)" : "");
@@ -288,7 +297,7 @@ final class PerpsTab {
+ "account exactly once, including free capital in those accounts. Cross position requirements are "
+ "not added to owned collateral. Missing inputs make the affected total unavailable. "
+ "Successfully read empty scopes total zero. Totals can include last-success observations retained "
+ "after failures; inspect venue read status. Ratios are not aggregated.";
+ "after failures; inspect wallet and venue read status. Ratios are not aggregated.";
private static final String LONG_ROW_STYLE = "-fx-control-inner-background: #e6f4ea;"
+ "-fx-control-inner-background-alt: #e6f4ea;";
private static final String SHORT_ROW_STYLE = "-fx-control-inner-background: #fce8e6;"