A Spring Boot service for tracking personal finance data, including assets and the transactions used to build an investment portfolio.
The first version uses SQLite for local persistence and Flyway for database migrations. All identifiers are UUIDs; the application does not use auto-incrementing numeric IDs.
- Java 25
- Spring Boot 4.0.1
- Maven
- SQLite
- Flyway
- Spring JDBC
This is a Maven multi-module project:
finance-domain/ Domain records and enums
finance-persistence/ Database dependencies and persistence layer foundation
finance-application/ Executable Spring Boot application
The application uses a local SQLite database at data/asset-allocation.db by default. The directory must exist before starting the application:
New-Item -ItemType Directory -Force dataThe location can be overridden with the ASSET_ALLOCATION_DB environment variable.
Flyway creates the following tables on startup:
Stores financial instruments and their metadata:
id— UUID stored as textname— human-readable asset nameisin— unique ISINticker— optional exchange tickercurrency— ISO currency codecreated_at,updated_at— timestamps
Stores purchases and sales:
id— UUID stored as textasset_id— UUID reference toassettransaction_type—BUYorSELLtransaction_date— date of the tradequantity— number of unitsprice— price per unitcurrency— transaction currencyfees— transaction fees, defaulting to zeronotes— optional notescreated_at— creation timestamp
Quantities and monetary values are represented with BigDecimal in Java to avoid floating-point rounding errors.
Stores historical price observations. The current value of an asset is the most recent observation by observed_at; older observations are retained for portfolio valuation and charts.
id— UUID stored as textasset_id— UUID reference toassetobserved_at— timestamp of the market observation, stored in UTCprice— observed price or index valuecurrency— price currencysource— optional provider or broker namecreated_at— insertion timestamp
Stores the financial brokers used for transactions. Broker records are created explicitly by the application or through SQL.
Transactions can reference a broker through the nullable broker_id column. It is nullable to keep previously stored transactions valid.
From the repository root:
mvn testThe integration test starts the application context with a test SQLite database and verifies that both finance tables are created by Flyway.
Create the database directory and start the executable module:
New-Item -ItemType Directory -Force data
mvn -pl finance-application -am package
java -jar finance-application\target\finance-application-0.0.1-SNAPSHOT.jarThe server listens on port 8080 by default. The health endpoint is:
GET http://localhost:8080/actuator/health
The current implementation establishes the server, database connection, migrations, domain foundation, and an asset-state endpoint. Asset and transaction-management services will be added next.
The current state of an asset can be retrieved with:
GET /api/assets/{assetId}/state
The response includes current quantity, bought and sold quantities, buy/sell amounts, fees, invested amount, average buy price, latest stored market price, current value, unrealized profit percentage, expected tax, profit without tax, and profit without tax and fees. Expected tax is calculated only for a positive unrealized profit using the asset's asset_tax.tax_rate_percent value.
The first market-data adapter uses Yahoo Finance's chart endpoint. Set an asset's ticker to the Yahoo Finance symbol, including the exchange suffix when required. For the EUR Xetra listing of the ETF with ISIN IE00BFNM3J75, use SNAW.DE.
The adapter returns the latest available price, timestamp, currency, and source through the MarketDataProvider interface. Yahoo Finance does not require an API key for this endpoint, but the endpoint is not an officially guaranteed public market-data API and may return delayed data or become unavailable.
The application stores the latest available quote for every asset with a configured Yahoo ticker immediately after startup and then once every hour. The interval and initial delay can be changed with market-data.schedule.interval-ms and market-data.schedule.initial-delay-ms.