The AssetAZ Ticker and RaydiumPoolPriceSource can already provide the real EVE/USDT market price. The current EMC implementation still uses hardcoded EvelynStatusIndexPoint data and creates a static chart snapshot.
Price-index calculation
The expected EVE price starts at:
Price: 15.00 USD
Time: 2026-08-01T00:00
Time zone: Europe/Copenhagen
Corresponding instant: 2026-07-31T22:00:00Z
The expected price grows continuously along an exponential curve that produces exactly 20% growth per 365-day year:
elapsedSeconds is the elapsed duration from the configured start instant to the sampling instant. This is exponential growth, not linear interpolation.
Examples:
At the start:
expectedPrice = 15
After exactly 365 days:
expectedPrice = 18
After 18 days, 3 hours, 8 minutes and 14 seconds:
expectedPrice ≈ 15.136464436276
actualPrice == expectedPrice -> index 0
actualPrice is 10% above expected -> index 1
actualPrice is 10% below expected -> index -1
actualPrice is twice expected -> index 10
actualPrice is half of expected -> index -5
Use adequate decimal precision and an explicit MathContext where division requires it. Avoid arbitrary rounding of the value stored in the history or supplied to the chart.
The expected-price and index calculations must be isolated from the JavaFX presentation code and be deterministic/testable using an explicit Instant or injected Clock.
Calls before the configured start time do not need to be supported in version 1.
Obtain the actual price from AssetAZ Ticker
The actual price must come from:
TickerService.getLatestPrice(eveUsdtTradingPair)
Use the existing canonical EVE and USDT identities from the Currency Identity Service. Do not identify currencies using raw symbol strings or duplicate UUIDs/mint addresses.
The value to use is the price from the returned PriceObservation/AssetPrice.
For version 1, the EVE/USDT quote is treated directly as the USD price:
1 USDT == 1 USD
Currency conversion and depeg handling are outside this task.
EMC and Evelyn must not communicate directly with Raydium. The dependency direction must remain:
Raydium PriceSource -> TickerService -> Evelyn -> Evelyn Mission Control
The calculation and in-memory status-index history belong to Evelyn-owned code. EMC is responsible for visualizing that data, not for implementing the financial formula or accessing Raydium directly.
Sampling and in-memory history
Replace the hardcoded history currently returned by EvelynImpl.
For each Evelyn environment:
The status-index history starts empty.
Attempt to add the first point immediately after the required services and EMC have started.
Add another point approximately once per minute.
Use fixed-delay or equivalent non-overlapping scheduling; do not create catch-up bursts.
The point timestamp represents the sampling instant used for the expected-price calculation.
Add exactly one point for each successful sample.
Keep the points ordered from oldest to newest.
Keep all points for the lifetime of the current process; no runtime size limit is required in version 1.
Keep Production and Test histories as separate collections and separate chart series. They may contain identical values because they use the same market price and expected-price curve.
The history must exist only in memory:
Do not persist EvelynStatusIndexPoint data.
Do not restore or backfill index points from earlier runs.
Restarting the process must produce an empty chart that begins collecting new points.
Do not change the Ticker’s existing persistence behavior. Ticker price history and Evelyn index history are separate concepts.
getStatusIndexHistory() must return a safe, non-null snapshot and must remain safe while new points are being added.
Temporary price unavailability
Ticker startup and Raydium’s first asynchronous poll may race with the first index sample.
If TickerService.getLatestPrice(...) cannot yet return a price, or if one sampling attempt otherwise fails:
Do not insert a fake or zero-valued price-index point.
Do not crash Evelyn, EMC, JavaFX, or the Ticker.
Log enough context to diagnose the failed sample.
Retry normally at the next interval.
A temporary failure must not cancel future sampling.
Preserve the six-index API for future work
EvelynStatusIndexPoint currently contains all six planned indexes. Do not remove the five future fields.
For the real points created in this version:
Populate evelynPriceIndex with the calculated value.
Use BigDecimal.ZERO as temporary values for the other five fields.
In EvelynMissionControlImpl, only the following series must currently be enabled:
Evelyn Price Index
The five future series must not be deleted. Disable them by commenting out their existing chart.getData().add(...) statements:
EVE_SYRUP Pool Depth Index
EVE_SYRUP Pool Balance Index
AAZDKK_USDT Pool Balance Index
AAZDKK_USDT Pool Price Index
AAZDKK_USDT Pool Depth Index
Also disable/comment out their contributions to Y-axis bound calculation, so only the enabled Evelyn Price Index controls the visible range.
The API fields and the commented chart scaffolding must remain available for later implementation.
Live EMC chart behavior
The Production and Test Overview charts must update while EMC remains open. Reopening the window or restarting EMC must not be required to see new points.
The chart must:
Start correctly with an empty history.
Contain one enabled series named Evelyn Price Index.
Add successful samples on the JavaFX Application Thread.
Expand/update the timestamp axis when points are added.
Calculate the Y-axis range from the enabled price-index series.
Keep the Y-axis symmetric around 0.
Always display 0 as the desired state.
Continue using a reasonable default symmetric range when the history is empty or every index value is zero.
Avoid leaking scheduler/timer resources after the corresponding EMC window or sampling lifecycle has ended.
Do not allow background threads to mutate JavaFX chart objects directly.
NenjimHub composition and safety
In NenjimHubImpl, the services required by this feature are currently commented out deliberately.
Enable only the wiring necessary for:
CurrencyIdentityService
SolanaBlockChain
Raydium
The existing real EVE/USDT RaydiumPoolPriceSource
TickerService
Start the Ticker before Evelyn begins sampling and pass the public TickerService dependency and canonical EVE/USDT trading pair through the composition graph.
The unrelated HardcodedPriceSource is not required for this feature and may remain disabled.
Keep all unrelated services commented out, including alarms, Nenjim Composer, Nenjim Process Manager, Nenjim Test Tool, Soda Task Manager and Suwimo Client. Do not accidentally activate trading, alarms, transaction execution, or other side effects.
Continue relying on the existing Ticker history-file activation contract. Do not create, migrate, or change Ticker history files as part of this task.
OpenSpec
Create an OpenSpec change for this issue.
Update the existing evelyn-status-index-history capability so it no longer claims that all six lines are currently displayed. The canonical behavior after this task is:
The six typed fields remain part of EvelynStatusIndexPoint.
Only Evelyn Price Index is currently calculated and displayed.
The other five graph series remain present in source as disabled future scaffolding.
Price-index points are sampled during the current run and kept only in memory.
The expected-price curve, index formula, Ticker dependency and failure behavior are specified.
Do not change the generic Ticker or Raydium PriceSource contracts unless a real implementation blocker is discovered.
Verification
Add focused deterministic verification for at least:
Expected price at the start instant is 15.
Expected price after one 365-day year is 18.
The 18-day, 3-hour, 8-minute and 14-second example is approximately 15.136464436276.
Equal actual and expected price gives index 0.
A 10% premium gives index 1.
A 10% discount gives index -1.
Double price gives index 10.
Missing initial Ticker data creates no point and does not stop later sampling.
Histories start empty and remain separate for Production and Test.
Only Evelyn Price Index affects the graph’s Y-axis bounds.
An empty chart can be created and later updated successfully.
Use a stub/fake TickerService and controllable time for deterministic verification. Automated verification must not access Raydium or the network.
Finally:
Compile main and test source sets.
Run relevant tests or deterministic harnesses.
Run strict OpenSpec validation.
Run git diff --check.
Review the final diff for accidentally enabled services and unrelated changes.
Out of scope
Persisting or restoring Evelyn index history
Backfilling the graph from Ticker history
Implementing the other five indexes
Enabling their five graph series
Configurable start price, start time, annual growth or sampling interval
USDT/USD conversion or depeg handling
Raydium streaming
Changes to the existing Raydium polling implementation
Changes to generic Ticker persistence or PriceSource discovery
Trading actions, alarms or automatic rebalancing
## Background
The `Overview` tab in Evelyn Mission Control displays indexes describing how well-balanced Evelyn is. The desired value for every index is `0`.
This task implements the first real index: **Evelyn Price Index**.
Use commit [`[cf603b4](https://github.com/r35157/com_r35157_nenjim-hubd-impl_ref/commit/cf603b416e400027fa31c86300778028105584c3)`](https://github.com/r35157/com_r35157_nenjim-hubd-impl_ref/commit/cf603b416e400027fa31c86300778028105584c3) as the starting point.
The AssetAZ Ticker and `RaydiumPoolPriceSource` can already provide the real EVE/USDT market price. The current EMC implementation still uses hardcoded `EvelynStatusIndexPoint` data and creates a static chart snapshot.
## Price-index calculation
The expected EVE price starts at:
* Price: `15.00 USD`
* Time: `2026-08-01T00:00`
* Time zone: `Europe/Copenhagen`
* Corresponding instant: `2026-07-31T22:00:00Z`
The expected price grows continuously along an exponential curve that produces exactly 20% growth per 365-day year:
```text
expectedPrice =
15 * 1.20 ^ (elapsedSeconds / 31_536_000)
```
`elapsedSeconds` is the elapsed duration from the configured start instant to the sampling instant. This is exponential growth, not linear interpolation.
Examples:
```text
At the start:
expectedPrice = 15
After exactly 365 days:
expectedPrice = 18
After 18 days, 3 hours, 8 minutes and 14 seconds:
expectedPrice ≈ 15.136464436276
```
The Evelyn Price Index is:
```text
evelynPriceIndex =
((actualPrice / expectedPrice) - 1) * 10
```
Examples:
```text
actualPrice == expectedPrice -> index 0
actualPrice is 10% above expected -> index 1
actualPrice is 10% below expected -> index -1
actualPrice is twice expected -> index 10
actualPrice is half of expected -> index -5
```
Use adequate decimal precision and an explicit `MathContext` where division requires it. Avoid arbitrary rounding of the value stored in the history or supplied to the chart.
The expected-price and index calculations must be isolated from the JavaFX presentation code and be deterministic/testable using an explicit `Instant` or injected `Clock`.
Calls before the configured start time do not need to be supported in version 1.
## Obtain the actual price from AssetAZ Ticker
The actual price must come from:
```java
TickerService.getLatestPrice(eveUsdtTradingPair)
```
Use the existing canonical EVE and USDT identities from the Currency Identity Service. Do not identify currencies using raw symbol strings or duplicate UUIDs/mint addresses.
The value to use is the price from the returned `PriceObservation`/`AssetPrice`.
For version 1, the EVE/USDT quote is treated directly as the USD price:
```text
1 USDT == 1 USD
```
Currency conversion and depeg handling are outside this task.
EMC and Evelyn must not communicate directly with Raydium. The dependency direction must remain:
```text
Raydium PriceSource -> TickerService -> Evelyn -> Evelyn Mission Control
```
The calculation and in-memory status-index history belong to Evelyn-owned code. EMC is responsible for visualizing that data, not for implementing the financial formula or accessing Raydium directly.
## Sampling and in-memory history
Replace the hardcoded history currently returned by `EvelynImpl`.
For each Evelyn environment:
* The status-index history starts empty.
* Attempt to add the first point immediately after the required services and EMC have started.
* Add another point approximately once per minute.
* Use fixed-delay or equivalent non-overlapping scheduling; do not create catch-up bursts.
* The point timestamp represents the sampling instant used for the expected-price calculation.
* Add exactly one point for each successful sample.
* Keep the points ordered from oldest to newest.
* Keep all points for the lifetime of the current process; no runtime size limit is required in version 1.
* Keep Production and Test histories as separate collections and separate chart series. They may contain identical values because they use the same market price and expected-price curve.
The history must exist only in memory:
* Do not persist `EvelynStatusIndexPoint` data.
* Do not restore or backfill index points from earlier runs.
* Restarting the process must produce an empty chart that begins collecting new points.
* Do not change the Ticker’s existing persistence behavior. Ticker price history and Evelyn index history are separate concepts.
`getStatusIndexHistory()` must return a safe, non-null snapshot and must remain safe while new points are being added.
## Temporary price unavailability
Ticker startup and Raydium’s first asynchronous poll may race with the first index sample.
If `TickerService.getLatestPrice(...)` cannot yet return a price, or if one sampling attempt otherwise fails:
* Do not insert a fake or zero-valued price-index point.
* Do not crash Evelyn, EMC, JavaFX, or the Ticker.
* Log enough context to diagnose the failed sample.
* Retry normally at the next interval.
A temporary failure must not cancel future sampling.
## Preserve the six-index API for future work
`EvelynStatusIndexPoint` currently contains all six planned indexes. Do not remove the five future fields.
For the real points created in this version:
* Populate `evelynPriceIndex` with the calculated value.
* Use `BigDecimal.ZERO` as temporary values for the other five fields.
In `EvelynMissionControlImpl`, only the following series must currently be enabled:
```text
Evelyn Price Index
```
The five future series must not be deleted. Disable them by commenting out their existing `chart.getData().add(...)` statements:
```text
EVE_SYRUP Pool Depth Index
EVE_SYRUP Pool Balance Index
AAZDKK_USDT Pool Balance Index
AAZDKK_USDT Pool Price Index
AAZDKK_USDT Pool Depth Index
```
Also disable/comment out their contributions to Y-axis bound calculation, so only the enabled Evelyn Price Index controls the visible range.
The API fields and the commented chart scaffolding must remain available for later implementation.
## Live EMC chart behavior
The Production and Test `Overview` charts must update while EMC remains open. Reopening the window or restarting EMC must not be required to see new points.
The chart must:
* Start correctly with an empty history.
* Contain one enabled series named `Evelyn Price Index`.
* Add successful samples on the JavaFX Application Thread.
* Expand/update the timestamp axis when points are added.
* Calculate the Y-axis range from the enabled price-index series.
* Keep the Y-axis symmetric around `0`.
* Always display `0` as the desired state.
* Continue using a reasonable default symmetric range when the history is empty or every index value is zero.
* Avoid leaking scheduler/timer resources after the corresponding EMC window or sampling lifecycle has ended.
Do not allow background threads to mutate JavaFX chart objects directly.
## NenjimHub composition and safety
In `NenjimHubImpl`, the services required by this feature are currently commented out deliberately.
Enable only the wiring necessary for:
* `CurrencyIdentityService`
* `SolanaBlockChain`
* `Raydium`
* The existing real EVE/USDT `RaydiumPoolPriceSource`
* `TickerService`
Start the Ticker before Evelyn begins sampling and pass the public `TickerService` dependency and canonical EVE/USDT trading pair through the composition graph.
The unrelated `HardcodedPriceSource` is not required for this feature and may remain disabled.
Keep all unrelated services commented out, including alarms, Nenjim Composer, Nenjim Process Manager, Nenjim Test Tool, Soda Task Manager and Suwimo Client. Do not accidentally activate trading, alarms, transaction execution, or other side effects.
Continue relying on the existing Ticker history-file activation contract. Do not create, migrate, or change Ticker history files as part of this task.
## OpenSpec
Create an OpenSpec change for this issue.
Update the existing `evelyn-status-index-history` capability so it no longer claims that all six lines are currently displayed. The canonical behavior after this task is:
* The six typed fields remain part of `EvelynStatusIndexPoint`.
* Only Evelyn Price Index is currently calculated and displayed.
* The other five graph series remain present in source as disabled future scaffolding.
* Price-index points are sampled during the current run and kept only in memory.
* The expected-price curve, index formula, Ticker dependency and failure behavior are specified.
Do not change the generic Ticker or Raydium PriceSource contracts unless a real implementation blocker is discovered.
## Verification
Add focused deterministic verification for at least:
* Expected price at the start instant is `15`.
* Expected price after one 365-day year is `18`.
* The 18-day, 3-hour, 8-minute and 14-second example is approximately `15.136464436276`.
* Equal actual and expected price gives index `0`.
* A 10% premium gives index `1`.
* A 10% discount gives index `-1`.
* Double price gives index `10`.
* Missing initial Ticker data creates no point and does not stop later sampling.
* Histories start empty and remain separate for Production and Test.
* Only Evelyn Price Index affects the graph’s Y-axis bounds.
* An empty chart can be created and later updated successfully.
Use a stub/fake `TickerService` and controllable time for deterministic verification. Automated verification must not access Raydium or the network.
Finally:
* Compile main and test source sets.
* Run relevant tests or deterministic harnesses.
* Run strict OpenSpec validation.
* Run `git diff --check`.
* Review the final diff for accidentally enabled services and unrelated changes.
## Out of scope
* Persisting or restoring Evelyn index history
* Backfilling the graph from Ticker history
* Implementing the other five indexes
* Enabling their five graph series
* Configurable start price, start time, annual growth or sampling interval
* USDT/USD conversion or depeg handling
* Raydium streaming
* Changes to the existing Raydium polling implementation
* Changes to generic Ticker persistence or PriceSource discovery
* Trading actions, alarms or automatic rebalancing
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Background
The
Overviewtab in Evelyn Mission Control displays indexes describing how well-balanced Evelyn is. The desired value for every index is0.This task implements the first real index: Evelyn Price Index.
Use commit
[cf603b4](https://github.com/r35157/com_r35157_nenjim-hubd-impl_ref/commit/cf603b416e400027fa31c86300778028105584c3)as the starting point.The AssetAZ Ticker and
RaydiumPoolPriceSourcecan already provide the real EVE/USDT market price. The current EMC implementation still uses hardcodedEvelynStatusIndexPointdata and creates a static chart snapshot.Price-index calculation
The expected EVE price starts at:
15.00 USD2026-08-01T00:00Europe/Copenhagen2026-07-31T22:00:00ZThe expected price grows continuously along an exponential curve that produces exactly 20% growth per 365-day year:
elapsedSecondsis the elapsed duration from the configured start instant to the sampling instant. This is exponential growth, not linear interpolation.Examples:
The Evelyn Price Index is:
Examples:
Use adequate decimal precision and an explicit
MathContextwhere division requires it. Avoid arbitrary rounding of the value stored in the history or supplied to the chart.The expected-price and index calculations must be isolated from the JavaFX presentation code and be deterministic/testable using an explicit
Instantor injectedClock.Calls before the configured start time do not need to be supported in version 1.
Obtain the actual price from AssetAZ Ticker
The actual price must come from:
Use the existing canonical EVE and USDT identities from the Currency Identity Service. Do not identify currencies using raw symbol strings or duplicate UUIDs/mint addresses.
The value to use is the price from the returned
PriceObservation/AssetPrice.For version 1, the EVE/USDT quote is treated directly as the USD price:
Currency conversion and depeg handling are outside this task.
EMC and Evelyn must not communicate directly with Raydium. The dependency direction must remain:
The calculation and in-memory status-index history belong to Evelyn-owned code. EMC is responsible for visualizing that data, not for implementing the financial formula or accessing Raydium directly.
Sampling and in-memory history
Replace the hardcoded history currently returned by
EvelynImpl.For each Evelyn environment:
The history must exist only in memory:
EvelynStatusIndexPointdata.getStatusIndexHistory()must return a safe, non-null snapshot and must remain safe while new points are being added.Temporary price unavailability
Ticker startup and Raydium’s first asynchronous poll may race with the first index sample.
If
TickerService.getLatestPrice(...)cannot yet return a price, or if one sampling attempt otherwise fails:A temporary failure must not cancel future sampling.
Preserve the six-index API for future work
EvelynStatusIndexPointcurrently contains all six planned indexes. Do not remove the five future fields.For the real points created in this version:
evelynPriceIndexwith the calculated value.BigDecimal.ZEROas temporary values for the other five fields.In
EvelynMissionControlImpl, only the following series must currently be enabled:The five future series must not be deleted. Disable them by commenting out their existing
chart.getData().add(...)statements:Also disable/comment out their contributions to Y-axis bound calculation, so only the enabled Evelyn Price Index controls the visible range.
The API fields and the commented chart scaffolding must remain available for later implementation.
Live EMC chart behavior
The Production and Test
Overviewcharts must update while EMC remains open. Reopening the window or restarting EMC must not be required to see new points.The chart must:
Evelyn Price Index.0.0as the desired state.Do not allow background threads to mutate JavaFX chart objects directly.
NenjimHub composition and safety
In
NenjimHubImpl, the services required by this feature are currently commented out deliberately.Enable only the wiring necessary for:
CurrencyIdentityServiceSolanaBlockChainRaydiumRaydiumPoolPriceSourceTickerServiceStart the Ticker before Evelyn begins sampling and pass the public
TickerServicedependency and canonical EVE/USDT trading pair through the composition graph.The unrelated
HardcodedPriceSourceis not required for this feature and may remain disabled.Keep all unrelated services commented out, including alarms, Nenjim Composer, Nenjim Process Manager, Nenjim Test Tool, Soda Task Manager and Suwimo Client. Do not accidentally activate trading, alarms, transaction execution, or other side effects.
Continue relying on the existing Ticker history-file activation contract. Do not create, migrate, or change Ticker history files as part of this task.
OpenSpec
Create an OpenSpec change for this issue.
Update the existing
evelyn-status-index-historycapability so it no longer claims that all six lines are currently displayed. The canonical behavior after this task is:EvelynStatusIndexPoint.Do not change the generic Ticker or Raydium PriceSource contracts unless a real implementation blocker is discovered.
Verification
Add focused deterministic verification for at least:
15.18.15.136464436276.0.1.-1.10.Use a stub/fake
TickerServiceand controllable time for deterministic verification. Automated verification must not access Raydium or the network.Finally:
git diff --check.Out of scope