AssetAZ needs a reusable Ticker service that can collect, persist and expose asset prices.
The Ticker is a general AssetAZ sub-service. It is not owned by Evelyn and must not contain Evelyn-specific logic. Evelyn, alarm services, portfolio services and other plugins may consume the Ticker in future changes.
The code will initially live in the NenjimHub implementation repository because this repository currently also acts as Cauldron for immature modules. Its physical location does not make it part of NenjimHub.
Goal
Implement the first complete version of the AssetAZ Ticker service.
The initial reference implementation shall:
support the EVE_USDC trading pair
produce a hardcoded price of 14.85
produce one observation immediately at startup
produce another observation once per minute
persist observations in a human-editable history file
expose only successfully persisted observations through the service API
be started by NenjimHubImpl
No consumer integration shall be introduced in this issue.
The initial implementation shall contain an internal hardcoded source for:
EVE_USDC = 14.85
The hardcoded source is temporary and exists only to establish the complete Ticker data flow. A general datasource-plugin API and real market sources are outside this issue.
History file
Use the following file:
data/assetaz/ticker/EVE_USDC.prices
The filename represents the TradingPair.
Each observation shall use this format:
<UTC timestamp>:<price>
The timestamp format shall be:
uuuuMMddHHmmssSSS'Z'
Example:
20260805131542783Z:14.85
This represents a UTC timestamp with millisecond precision and is deliberately human-readable and independent of daylight-saving time.
Human-editable file format
History files shall allow:
empty lines
full-line comments
inline comments
A full-line comment begins when the first non-whitespace character is #.
An inline comment begins at the first # on a data line.
Example:
20260802120000000Z:14.85
#### The following is a test price only
20260803120000000Z:15.85 # Manually inserted test price
####
20260804120000000Z:14.85
After comments are removed, every non-empty data line must contain a valid UTC timestamp and price.
A malformed data line shall make Ticker startup fail with an error containing:
the history filename
the line number
enough information to identify the invalid content
Malformed data must not be ignored silently.
Automatic Ticker writes shall append plain data lines only. Existing comments and blank lines shall remain untouched.
Explicit symbol activation
The existence of a history file is an explicit operator decision to enable that trading pair.
The Ticker shall service EVE_USDC only when:
data/assetaz/ticker/EVE_USDC.prices
already exists when the pair is initialized.
Existing non-empty file
The Ticker shall:
load the existing history
determine the latest observation by timestamp
make that persisted observation available
start producing new observations
Existing empty file
An empty file explicitly enables a new or restarted history.
The Ticker shall:
enable the trading pair
initially have no available price
persist the first generated observation
publish it only after successful persistence
Missing file
When the file does not exist, the Ticker shall:
not create it automatically
leave EVE_USDC unavailable
log a warning containing the trading pair and expected filepath
continue starting without that trading pair
A missing file will normally indicate a deployment or configuration error, such as an incorrect data directory, missing installation files or an unexpected working directory.
Automatically creating a new file could hide that problem and silently start a new incomplete price history.
To enable a new pair or intentionally restart its history, the operator must create the corresponding .prices file manually. The file may be empty.
Persist before publish
A newly generated observation must become available through TickerService only after it has been successfully persisted.
The processing order shall be:
Validate the observation.
Append it to the history file.
Flush/force the persisted data.
Update the latest in-memory observation.
Make the observation available through getLatestPrice(...).
If persistence fails:
the new observation must not be published
the previously persisted observation must remain available
the failure must be reported clearly
Concurrent reads must never observe an unpersisted price.
Startup
NenjimHubImpl shall start one TickerServiceImpl instance using the current temporary hardcoded autorun mechanism.
The Ticker is started by NenjimHub only because the repository currently acts as Cauldron. This does not establish logical ownership by NenjimHub.
Out of scope
Integration with Evelyn
Changes to Evelyn or EvelynImpl
Evelyn Price Index calculation
Integration with alarms, EMC or portfolio services
A datasource-plugin API
Real market-price sources
Additional trading pairs
Streaming sources
Subscriptions or callbacks
Public history-query APIs
Retention, compaction or downsampling
Automated tests
## Background
AssetAZ needs a reusable Ticker service that can collect, persist and expose asset prices.
The Ticker is a general AssetAZ sub-service. It is not owned by Evelyn and must not contain Evelyn-specific logic. Evelyn, alarm services, portfolio services and other plugins may consume the Ticker in future changes.
The code will initially live in the NenjimHub implementation repository because this repository currently also acts as Cauldron for immature modules. Its physical location does not make it part of NenjimHub.
## Goal
Implement the first complete version of the AssetAZ Ticker service.
The initial reference implementation shall:
* support the `EVE_USDC` trading pair
* produce a hardcoded price of `14.85`
* produce one observation immediately at startup
* produce another observation once per minute
* persist observations in a human-editable history file
* expose only successfully persisted observations through the service API
* be started by `NenjimHubImpl`
No consumer integration shall be introduced in this issue.
## API
Create the service interface:
```java
com.r35157.assetaz.core.service.ticker.TickerService
```
with an API equivalent to:
```java
public interface TickerService {
void start();
PriceObservation getLatestPrice(TradingPair tradingPair);
}
```
Create the observation value type:
```java
com.r35157.assetaz.core.service.ticker.PriceObservation
```
```java
public record PriceObservation(
AssetPrice price,
Instant observedAt
) {
}
```
Use the existing ValueTypes:
```java
com.r35157.libs.valuetypes.basic.AssetPrice
com.r35157.libs.valuetypes.basic.TradingPair
com.r35157.libs.valuetypes.basic.ΩPriceΩ
```
`AssetPrice` already contains both the price and its `TradingPair`; no separate symbol field shall be added to `PriceObservation`.
An unavailable trading pair or an enabled pair without a persisted price shall produce a clear exception rather than returning `null`.
## Reference implementation
Create:
```java
com.r35157.assetaz.core.service.ticker.impl.ref.TickerServiceImpl
```
The initial implementation shall contain an internal hardcoded source for:
```text
EVE_USDC = 14.85
```
The hardcoded source is temporary and exists only to establish the complete Ticker data flow. A general datasource-plugin API and real market sources are outside this issue.
## History file
Use the following file:
```text
data/assetaz/ticker/EVE_USDC.prices
```
The filename represents the `TradingPair`.
Each observation shall use this format:
```text
<UTC timestamp>:<price>
```
The timestamp format shall be:
```text
uuuuMMddHHmmssSSS'Z'
```
Example:
```text
20260805131542783Z:14.85
```
This represents a UTC timestamp with millisecond precision and is deliberately human-readable and independent of daylight-saving time.
## Human-editable file format
History files shall allow:
* empty lines
* full-line comments
* inline comments
A full-line comment begins when the first non-whitespace character is `#`.
An inline comment begins at the first `#` on a data line.
Example:
```text
20260802120000000Z:14.85
#### The following is a test price only
20260803120000000Z:15.85 # Manually inserted test price
####
20260804120000000Z:14.85
```
After comments are removed, every non-empty data line must contain a valid UTC timestamp and price.
A malformed data line shall make Ticker startup fail with an error containing:
* the history filename
* the line number
* enough information to identify the invalid content
Malformed data must not be ignored silently.
Automatic Ticker writes shall append plain data lines only. Existing comments and blank lines shall remain untouched.
## Explicit symbol activation
The existence of a history file is an explicit operator decision to enable that trading pair.
The Ticker shall service `EVE_USDC` only when:
```text
data/assetaz/ticker/EVE_USDC.prices
```
already exists when the pair is initialized.
### Existing non-empty file
The Ticker shall:
* load the existing history
* determine the latest observation by timestamp
* make that persisted observation available
* start producing new observations
### Existing empty file
An empty file explicitly enables a new or restarted history.
The Ticker shall:
* enable the trading pair
* initially have no available price
* persist the first generated observation
* publish it only after successful persistence
### Missing file
When the file does not exist, the Ticker shall:
* not create it automatically
* leave `EVE_USDC` unavailable
* log a warning containing the trading pair and expected filepath
* continue starting without that trading pair
A missing file will normally indicate a deployment or configuration error, such as an incorrect data directory, missing installation files or an unexpected working directory.
Automatically creating a new file could hide that problem and silently start a new incomplete price history.
To enable a new pair or intentionally restart its history, the operator must create the corresponding `.prices` file manually. The file may be empty.
## Persist before publish
A newly generated observation must become available through `TickerService` only after it has been successfully persisted.
The processing order shall be:
1. Validate the observation.
2. Append it to the history file.
3. Flush/force the persisted data.
4. Update the latest in-memory observation.
5. Make the observation available through `getLatestPrice(...)`.
If persistence fails:
* the new observation must not be published
* the previously persisted observation must remain available
* the failure must be reported clearly
Concurrent reads must never observe an unpersisted price.
## Startup
`NenjimHubImpl` shall start one `TickerServiceImpl` instance using the current temporary hardcoded autorun mechanism.
The Ticker is started by NenjimHub only because the repository currently acts as Cauldron. This does not establish logical ownership by NenjimHub.
## Out of scope
* Integration with Evelyn
* Changes to `Evelyn` or `EvelynImpl`
* Evelyn Price Index calculation
* Integration with alarms, EMC or portfolio services
* A datasource-plugin API
* Real market-price sources
* Additional trading pairs
* Streaming sources
* Subscriptions or callbacks
* Public history-query APIs
* Retention, compaction or downsampling
* Automated tests
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
AssetAZ needs a reusable Ticker service that can collect, persist and expose asset prices.
The Ticker is a general AssetAZ sub-service. It is not owned by Evelyn and must not contain Evelyn-specific logic. Evelyn, alarm services, portfolio services and other plugins may consume the Ticker in future changes.
The code will initially live in the NenjimHub implementation repository because this repository currently also acts as Cauldron for immature modules. Its physical location does not make it part of NenjimHub.
Goal
Implement the first complete version of the AssetAZ Ticker service.
The initial reference implementation shall:
EVE_USDCtrading pair14.85NenjimHubImplNo consumer integration shall be introduced in this issue.
API
Create the service interface:
with an API equivalent to:
Create the observation value type:
Use the existing ValueTypes:
AssetPricealready contains both the price and itsTradingPair; no separate symbol field shall be added toPriceObservation.An unavailable trading pair or an enabled pair without a persisted price shall produce a clear exception rather than returning
null.Reference implementation
Create:
The initial implementation shall contain an internal hardcoded source for:
The hardcoded source is temporary and exists only to establish the complete Ticker data flow. A general datasource-plugin API and real market sources are outside this issue.
History file
Use the following file:
The filename represents the
TradingPair.Each observation shall use this format:
The timestamp format shall be:
Example:
This represents a UTC timestamp with millisecond precision and is deliberately human-readable and independent of daylight-saving time.
Human-editable file format
History files shall allow:
A full-line comment begins when the first non-whitespace character is
#.An inline comment begins at the first
#on a data line.Example:
After comments are removed, every non-empty data line must contain a valid UTC timestamp and price.
A malformed data line shall make Ticker startup fail with an error containing:
Malformed data must not be ignored silently.
Automatic Ticker writes shall append plain data lines only. Existing comments and blank lines shall remain untouched.
Explicit symbol activation
The existence of a history file is an explicit operator decision to enable that trading pair.
The Ticker shall service
EVE_USDConly when:already exists when the pair is initialized.
Existing non-empty file
The Ticker shall:
Existing empty file
An empty file explicitly enables a new or restarted history.
The Ticker shall:
Missing file
When the file does not exist, the Ticker shall:
EVE_USDCunavailableA missing file will normally indicate a deployment or configuration error, such as an incorrect data directory, missing installation files or an unexpected working directory.
Automatically creating a new file could hide that problem and silently start a new incomplete price history.
To enable a new pair or intentionally restart its history, the operator must create the corresponding
.pricesfile manually. The file may be empty.Persist before publish
A newly generated observation must become available through
TickerServiceonly after it has been successfully persisted.The processing order shall be:
getLatestPrice(...).If persistence fails:
Concurrent reads must never observe an unpersisted price.
Startup
NenjimHubImplshall start oneTickerServiceImplinstance using the current temporary hardcoded autorun mechanism.The Ticker is started by NenjimHub only because the repository currently acts as Cauldron. This does not establish logical ownership by NenjimHub.
Out of scope
EvelynorEvelynImpl