Cryptolyst is a self-hosted cryptocurrency trade journal and portfolio analytics application. It keeps each asset and buy lot separate, calculates realized and unrealized profit with decimal arithmetic, tracks partial sales and profit targets, and refreshes market prices automatically.
- Asset-grouped investment ledgers with independent charts
- Buy lots, unlimited partial sales, fees, exchanges, accounts, and notes
- Remaining cost, average entry, break-even status, realized and unrealized P&L
- Per-lot profit targets and target-price tracking
- CoinGecko and Binance public price providers
- Configurable background price refresh interval
- Per-asset colors and coin icons
- Light, dark, and system themes
- CSV transaction import and export
- Full JSON backup export and restore into a new database
- Legacy spreadsheet migration utility
- Single-user password authentication with bcrypt
- SQLite persistence with no external database service required
git clone https://github.com/ezn24/cryptolyst.git
cd cryptolystcp .env.example .envGenerate a bcrypt password hash:
npm install
npm run hash-password -- "choose-a-strong-password"Copy the printed Docker Compose .env value to APP_PASSWORD_HASH, then generate a session secret:
openssl rand -hex 32Your .env should contain at least:
APP_PASSWORD_HASH=$$2b$$12$$...
SESSION_SECRET=replace-with-at-least-32-random-charactersdocker compose up -dOpen http://localhost:3000.
The Compose project uses the cryptolyst-data named volume for the SQLite database. Container recreation and image updates do not remove this volume.
docker compose pull
docker compose up -d --force-recreateTo pin a specific image version, set CRYPTOLYST_IMAGE in .env:
CRYPTOLYST_IMAGE=1030283726/cryptolyst:sha-6224090docker volume create cryptolyst-data
docker run -d \
--name cryptolyst \
--restart unless-stopped \
-p 3000:3000 \
-e DATABASE_URL=file:/data/cryptolyst.db \
-e APP_PASSWORD_HASH='\$2b\$12\$...' \
-e SESSION_SECRET='replace-with-at-least-32-random-characters' \
-v cryptolyst-data:/data \
1030283726/cryptolyst:latestdocker build -t cryptolyst:local .Run the locally built image:
docker run -d \
--name cryptolyst \
-p 3000:3000 \
-e DATABASE_URL=file:/data/cryptolyst.db \
-e APP_PASSWORD_HASH='\$2b\$12\$...' \
-e SESSION_SECRET='replace-with-at-least-32-random-characters' \
-v cryptolyst-data:/data \
cryptolyst:local| Variable | Default | Description |
|---|---|---|
DATABASE_URL |
file:/data/cryptolyst.db |
Prisma SQLite URL inside the container |
APP_PASSWORD_HASH |
Required | bcrypt $2a$, $2b$, or $2y$ password hash |
SESSION_SECRET |
Required | Random session-signing secret of at least 32 characters |
PRICE_PROVIDER |
coingecko |
Default market price provider |
PRICE_REFRESH_INTERVAL_MINUTES |
5 |
Background price refresh interval |
TZ |
UTC |
Container timezone |
TRUST_PROXY |
true |
Enables operation behind a trusted reverse proxy |
PORT |
3000 |
Host port used by compose.yml |
CRYPTOLYST_IMAGE |
1030283726/cryptolyst:latest |
Image used by compose.yml |
The application settings page can change the price provider, refresh interval, timezone, theme, decimal precision, and other display preferences.
Requirements: Node.js 24 and npm.
npm install
cp .env.example .env
npm run db:generate
npm run db:init
npm run seed
npm run devOpen http://localhost:3000.
Useful commands:
npm run lint
npm run typecheck
npm test
npm run buildCryptolyst stores application data in SQLite. The Docker image uses /data/cryptolyst.db, persisted by the cryptolyst-data named volume.
The recommended portable backup workflow is available in Import / Export:
- Download a complete JSON backup.
- Store the file outside the container.
- Import it into a new, empty Cryptolyst database when restoration is required.
JSON backups contain assets, buy lots, sales, profit targets, price history, and application settings. JSON restore refuses to write into a database that already contains portfolio data.
CSV imports support BUY and SELL rows. Required columns are:
type,asset,date,price,quantity
Optional columns include:
fee,feeCurrency,exchange,account,note,buyLotReference
SELL rows must provide the target buy-lot ID in buyLotReference.
Preview an existing workbook without changing the database:
npm run import:legacy -- "/path/to/crypto-trades.xlsx"Commit the validated import:
npx tsx scripts/import-legacy-xlsx.ts "/path/to/crypto-trades.xlsx" --commitUse --replace-existing only when intentionally rebuilding the database from the workbook.
The server starts its price scheduler automatically. It performs the first update shortly after startup and then uses the configured refresh interval. Failed provider requests retain the last valid price.
- CoinGecko uses each asset's CoinGecko ID.
- Binance uses each asset's Binance symbol.
- Manual prices remain available from the price-management page.
To populate missing CoinGecko icons:
npm run sync-icons- Next.js App Router and React Server Components
- TypeScript and Server Actions
- Tailwind CSS, Lucide Icons, and Recharts
- Prisma ORM with SQLite
decimal.jsfor financial calculations- bcrypt password verification and signed HttpOnly sessions
The GitHub Actions workflow validates every relevant source change with type checking, linting, and tests, then publishes the Docker image to Docker Hub.
Published tags include:
latestfor the default branchsha-<commit>for each build- Semantic-version tags for Git tags such as
v1.2.0
- Keep
.envoutside version control. - Use a unique password and a randomly generated session secret.
- Cryptolyst does not require exchange API keys or private keys.
- All mutations require an authenticated session.
- Financial calculations run on the server using decimal arithmetic.
This project is licensed under the GNU General Public License v3.0 (GPL-3.0). See LICENSE.