Reduces unnecessary upserts on each 15-second poll cycle by only
processing the 5 most recent candles from Kraken's response.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1-minute OHLC candles from Kraken, polled every 15 seconds to keep the
in-progress candle fresh. Unlike hourly/daily pollers, this one keeps
the last candle and upserts it as it updates.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Same pattern as BINANCE_ENABLED and STRIKE_ENABLED. Prevents local
dev from fetching external pricing data when pointed at btcprod.
Docker explicitly sets KRAKEN_ENABLED=true.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add POST /api/deposits/rebuild to re-project all bank_to_exchange events.
Fix date-off-by-one by converting java.sql.Date to LocalDate via
ReadableColumn and serializing with Jackson JavaTimeModule.
Sort all event/deposit queries by occurred_at, id.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Skip live WebSocket/API connections when set to false.
Defaults to true so Docker keeps fetching.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Exchanges (can receive bank deposits) vs wallets (cold storage only).
Supports ?type= filter on GET /api/wallets.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Project each bank_to_exchange event into a denormalized deposits table
with pre-computed EUR, DKK, and USD amounts using Frankfurter currency
rates. Replaces the aggregate get-deposit-totals query.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
GET /api/deposits/totals returns fiat deposited per exchange per currency
from bank_to_exchange events, aggregated via SQL.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Wallets table with 7 seeded wallets, events table for three transaction
types (bank_to_exchange, exchange_to_wallet, wallet_to_wallet). Balances
computed from event credits/debits in sats. REST endpoints for CRUD.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Returns rates for a given date: today uses latest DB row, past dates
check DB first then fetch from Frankfurter and persist for caching.
Future dates and invalid formats return 400.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Store daily exchange rates in PostgreSQL with EUR/DKK cross rate.
Seed rates atom from DB on startup so WebSocket works immediately
even if Frankfurter is unreachable.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Use POST /v1/currency-exchange-quotes to get actual buy price including
spread, matching the Strike app. Fee is cached daily to calculate the
pre-fee amount from the configured total (default 55 EUR). Store
quote_sats in the strike_price DB table.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Price and Strike WebSockets now emit JSON instead of HTML fragments.
Added Strike WebSocket endpoint and wired strike/ticker into API routes.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- All services use btc-network exclusively
- Increase Frankfurter retry to 12x10s for cold start resilience
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Poll Strike API every 10s, store all rates as JSONB in strike_price table
- Move Docker env vars to .env file, use shared network for cross-project communication
- Add startup retry (5x 5s) for Frankfurter poller to handle container race condition
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Require frankfurter.rates namespace in core.clj so Integrant can find it
- Use /v1/ prefix for self-hosted Frankfurter API
- Display BTC price converted to EUR and DKK instead of raw exchange rates
- Remove unused host port mapping for Frankfurter container
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Polls self-hosted Frankfurter API hourly for USD-based EUR and DKK exchange
rates. Both the WebSocket HTML fragments and the /api/price/latest JSON
endpoint now include the currency rates alongside BTC price.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
ring-defaults middleware (wrap-content-type, wrap-xss-protection) was
converting the nil response from the raw WebSocket upgrade handler into
a partial response map (headers only, no status), causing the Undertow
adapter to attempt ending the already-upgraded exchange.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- WebSocket sends HTML fragments with id="price-display" instead of JSON
for direct HTMX OOB swap on the client
- Switch from ring-undertow-adapter ws wrapper to raw Undertow WebSocket
API (proxy) to fix single-connection limitation
- Keep JSON endpoint at /api/price/latest unchanged
- Update CLAUDE.md and README.md
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Replace SSE endpoint (/api/price/stream) with WebSocket (/api/price/ws)
using ring-undertow-adapter's built-in WebSocket support
- Change Docker internal port from 4100 to 4101 to avoid conflicts with
local dev (make run still uses 4100)
- Add free-port.sh utility script
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Use Undertow dispatch for SSE handler to avoid Ring adapter conflict.
Add docker-compose.yml (port 4101 to coexist with local dev on 4100).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Split from btcprice monolith. Owns DB, migrations, Binance WebSocket,
Kraken OHLC pollers, and exposes a JSON API with SSE price stream.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>