Add buys query table from exchange_to_wallet events
Denormalized read table with id, event_id, occurred_at, wallet, sats, fee_sats, and amount_eur. Projected on event creation and rebuildable via POST /api/buys/rebuild. Fiat amounts converted to EUR using Frankfurter rates. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1 @@
|
||||
DROP TABLE IF EXISTS buys;
|
||||
@@ -0,0 +1,7 @@
|
||||
CREATE TABLE buys (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
event_id BIGINT NOT NULL REFERENCES events(id) UNIQUE,
|
||||
occurred_at DATE NOT NULL,
|
||||
wallet TEXT NOT NULL,
|
||||
amount_eur NUMERIC(18,2) NOT NULL
|
||||
);
|
||||
@@ -0,0 +1,3 @@
|
||||
ALTER TABLE buys DROP COLUMN fee_sats;
|
||||
--;;
|
||||
ALTER TABLE buys DROP COLUMN sats;
|
||||
@@ -0,0 +1,3 @@
|
||||
ALTER TABLE buys ADD COLUMN sats BIGINT NOT NULL DEFAULT 0;
|
||||
--;;
|
||||
ALTER TABLE buys ADD COLUMN fee_sats BIGINT;
|
||||
@@ -155,6 +155,30 @@ SELECT id, event_id, occurred_at, exchange, fiat_amount, fiat_currency,
|
||||
FROM deposits
|
||||
ORDER BY occurred_at DESC, id DESC
|
||||
|
||||
-- :name truncate-buys! :! :n
|
||||
-- :doc Delete all rows from the buys read table
|
||||
TRUNCATE buys
|
||||
|
||||
-- :name get-buy-events :? :*
|
||||
-- :doc Get exchange_to_wallet events with wallet name, oldest first
|
||||
SELECT e.id, e.occurred_at, e.sats, e.fee_sats, e.fiat_amount, e.fiat_currency,
|
||||
e.to_wallet_id, tw.name AS wallet
|
||||
FROM events e
|
||||
LEFT JOIN wallets tw ON tw.id = e.to_wallet_id
|
||||
WHERE e.event_type = 'exchange_to_wallet'
|
||||
ORDER BY e.occurred_at ASC, e.id ASC
|
||||
|
||||
-- :name insert-buy! :! :n
|
||||
-- :doc Insert a projected buy row
|
||||
INSERT INTO buys (event_id, occurred_at, wallet, sats, fee_sats, amount_eur)
|
||||
VALUES (:event-id, :occurred-at, :wallet, :sats, :fee-sats, :amount-eur)
|
||||
|
||||
-- :name get-all-buys :? :*
|
||||
-- :doc Get all projected buys ordered by date descending
|
||||
SELECT id, event_id, occurred_at, wallet, sats, fee_sats, amount_eur
|
||||
FROM buys
|
||||
ORDER BY occurred_at DESC, id DESC
|
||||
|
||||
-- :name get-kraken-hour-latest-24 :? :*
|
||||
-- :doc Get the 24 most recent Kraken hourly candles
|
||||
SELECT ts, open, high, low, close, vwap, volume, trade_count, created_at
|
||||
|
||||
@@ -69,6 +69,29 @@
|
||||
(catch Exception e
|
||||
(log/error e "Failed to project deposit for event" event-id))))
|
||||
|
||||
(defn- project-buy!
|
||||
"Project an exchange_to_wallet event into the buys read table."
|
||||
[{:keys [query-fn frankfurter]} event-id occurred-at to-wallet-id sats fee-sats fiat-amount fiat-currency]
|
||||
(try
|
||||
(let [wallet-name (or (:name (query-fn :get-wallet-by-id {:id to-wallet-id})) "Unknown")
|
||||
amount-eur (if (= fiat-currency "EUR")
|
||||
(bigdec fiat-amount)
|
||||
(let [rates (or (query-fn :get-currency-rates-by-date {:rate-date occurred-at})
|
||||
(rates/fetch-and-persist-for-date!
|
||||
(:client frankfurter) (:url frankfurter)
|
||||
query-fn occurred-at))]
|
||||
(:amount-eur (convert-amount fiat-amount fiat-currency rates))))]
|
||||
(when amount-eur
|
||||
(query-fn :insert-buy!
|
||||
{:event-id event-id
|
||||
:occurred-at occurred-at
|
||||
:wallet wallet-name
|
||||
:sats (or sats 0)
|
||||
:fee-sats fee-sats
|
||||
:amount-eur amount-eur})))
|
||||
(catch Exception e
|
||||
(log/error e "Failed to project buy for event" event-id))))
|
||||
|
||||
(defn create-event! [{:keys [query-fn] :as opts} req]
|
||||
(let [params (:body-params req)
|
||||
event-type (:event_type params)]
|
||||
@@ -93,6 +116,13 @@
|
||||
(:fiat_amount params)
|
||||
(:fiat_currency params)
|
||||
(:note params)))
|
||||
(when (and (= event-type "exchange_to_wallet") (:fiat_amount params))
|
||||
(project-buy! opts (:id result) occurred-at
|
||||
(some-> (:to_wallet_id params) UUID/fromString)
|
||||
(:sats params)
|
||||
(:fee_sats params)
|
||||
(:fiat_amount params)
|
||||
(:fiat_currency params)))
|
||||
(response/created "/api/events" {:status "ok"})))))
|
||||
|
||||
(defn list-deposits [{:keys [query-fn]} _req]
|
||||
@@ -128,6 +158,40 @@
|
||||
0 events)]
|
||||
(response/ok {:rebuilt n})))
|
||||
|
||||
(defn list-buys [{:keys [query-fn]} _req]
|
||||
(response/ok (query-fn :get-all-buys {})))
|
||||
|
||||
(defn rebuild-buys! [{:keys [query-fn frankfurter]} _req]
|
||||
(query-fn :truncate-buys! {})
|
||||
(let [events (query-fn :get-buy-events {})
|
||||
n (reduce
|
||||
(fn [cnt {:keys [id occurred_at sats fee_sats fiat_amount fiat_currency wallet]}]
|
||||
(if-not fiat_amount
|
||||
cnt
|
||||
(try
|
||||
(let [amount-eur
|
||||
(if (= fiat_currency "EUR")
|
||||
(bigdec fiat_amount)
|
||||
(let [rates (or (query-fn :get-currency-rates-by-date {:rate-date occurred_at})
|
||||
(rates/fetch-and-persist-for-date!
|
||||
(:client frankfurter) (:url frankfurter)
|
||||
query-fn occurred_at))]
|
||||
(:amount-eur (convert-amount fiat_amount fiat_currency rates))))]
|
||||
(when amount-eur
|
||||
(query-fn :insert-buy!
|
||||
{:event-id id
|
||||
:occurred-at occurred_at
|
||||
:wallet (or wallet "Unknown")
|
||||
:sats (or sats 0)
|
||||
:fee-sats fee_sats
|
||||
:amount-eur amount-eur}))
|
||||
(inc cnt))
|
||||
(catch Exception e
|
||||
(log/error e "Failed to rebuild buy for event" id)
|
||||
cnt))))
|
||||
0 events)]
|
||||
(response/ok {:rebuilt n})))
|
||||
|
||||
(defn list-events [{:keys [query-fn]} req]
|
||||
(let [event-type (get-in req [:query-params "type"])]
|
||||
(if event-type
|
||||
|
||||
@@ -202,6 +202,10 @@
|
||||
{:get (fn [req] (tx/list-deposits opts req))}]
|
||||
["/deposits/rebuild"
|
||||
{:post (fn [req] (tx/rebuild-deposits! opts req))}]
|
||||
["/buys"
|
||||
{:get (fn [req] (tx/list-buys opts req))}]
|
||||
["/buys/rebuild"
|
||||
{:post (fn [req] (tx/rebuild-buys! opts req))}]
|
||||
["/kraken-hour"
|
||||
{:get (fn [req] (kraken-hour-handler opts req))}]
|
||||
["/kraken-minute"
|
||||
|
||||
Reference in New Issue
Block a user