diff --git a/docker/grafana/provisioning/dashboards/routing.json b/docker/grafana/provisioning/dashboards/routing.json index 1bfd7050..92b66dee 100644 --- a/docker/grafana/provisioning/dashboards/routing.json +++ b/docker/grafana/provisioning/dashboards/routing.json @@ -4242,7 +4242,7 @@ "editorMode": "code", "format": "table", "rawQuery": true, - "rawSql": "WITH bounds AS (SELECT $__timeFrom()::timestamptz AS window_start, $__timeTo()::timestamptz AS window_end, GREATEST(EXTRACT(EPOCH FROM ($__timeTo()::timestamptz - $__timeFrom()::timestamptz)) / 86400.0, 0.000001) AS window_days), total_fees AS (SELECT COALESCE(ROUND(SUM(\"FeeMsat\") / 1000.0)::bigint, 0) AS fee_sats FROM \"ForwardingHtlcEvents\" WHERE $__timeFilter(\"EventTimestamp\") AND \"Outcome\" = 1 AND \"ManagedNodePubKey\" IN ($node) AND \"FeeMsat\" IS NOT NULL), total_yield_costs AS (SELECT COALESCE(SUM(yc.cost_sats), 0) AS yield_cost_sats FROM (SELECT s.\"NodeId\", $applySwapCosts * (COALESCE(s.\"ServiceFeeSats\", 0) + COALESCE(s.\"LightningFeeSats\", 0) + COALESCE(s.\"OnChainFeeSats\", 0)) AS cost_sats FROM \"SwapOuts\" s WHERE s.\"Status\" = 1 AND $__timeFilter(s.\"CreationDatetime\") UNION ALL SELECT r.\"NodeId\", $applyRebalanceCosts * COALESCE(r.\"FeePaidSats\", 0) AS cost_sats FROM \"Rebalances\" r WHERE r.\"Status\" = 3 AND r.\"FeePaidSats\" IS NOT NULL AND $__timeFilter(r.\"CreationDatetime\")) yc JOIN \"Nodes\" n ON n.\"Id\" = yc.\"NodeId\" WHERE n.\"PubKey\" IN ($node)), participating_chans AS (SELECT DISTINCT u.chan_id FROM \"ForwardingHtlcEvents\" f CROSS JOIN LATERAL (VALUES (f.\"IncomingChannelId\"), (f.\"OutgoingChannelId\")) AS u(chan_id) WHERE $__timeFilter(f.\"EventTimestamp\") AND f.\"Outcome\" = 1 AND f.\"ManagedNodePubKey\" IN ($node) AND f.\"FeeMsat\" IS NOT NULL), qualifying_liquidity AS (SELECT COALESCE(SUM(c.\"SatsAmount\" * GREATEST(EXTRACT(EPOCH FROM (LEAST(CASE WHEN c.\"Status\" = 2 THEN COALESCE((to_jsonb(c) ->> 'ClosedAt')::timestamptz, c.\"UpdateDatetime\") ELSE b.window_end END, b.window_end) - GREATEST(c.\"CreationDatetime\", b.window_start))) / 86400.0, 0.0) / b.window_days), 0) AS liquidity_sats FROM \"Channels\" c CROSS JOIN bounds b JOIN participating_chans p ON p.chan_id = c.\"ChanId\" WHERE (c.\"CreatedByNodeGuard\" = TRUE OR $liquidityScope = 0)) SELECT CASE WHEN ql.liquidity_sats > 0 THEN ((tf.fee_sats - tyc.yield_cost_sats)::numeric / ql.liquidity_sats) * (365.0 / b.window_days)::numeric ELSE NULL END AS \"APR\" FROM total_fees tf, total_yield_costs tyc, qualifying_liquidity ql, bounds b", + "rawSql": "WITH bounds AS (SELECT $__timeFrom()::timestamptz AS window_start, $__timeTo()::timestamptz AS window_end, GREATEST(EXTRACT(EPOCH FROM ($__timeTo()::timestamptz - $__timeFrom()::timestamptz)) / 86400.0, 0.000001) AS window_days), total_fees AS (SELECT COALESCE(ROUND(SUM(\"FeeMsat\") / 1000.0)::bigint, 0) AS fee_sats FROM \"ForwardingHtlcEvents\" WHERE $__timeFilter(\"EventTimestamp\") AND \"Outcome\" = 1 AND \"ManagedNodePubKey\" IN ($node) AND \"FeeMsat\" IS NOT NULL), total_yield_costs AS (SELECT COALESCE(SUM(yc.cost_sats), 0) AS yield_cost_sats FROM (SELECT s.\"NodeId\", $applySwapCosts * (COALESCE(s.\"ServiceFeeSats\", 0) + COALESCE(s.\"LightningFeeSats\", 0) + COALESCE(s.\"OnChainFeeSats\", 0)) AS cost_sats FROM \"SwapOuts\" s WHERE s.\"Status\" = 1 AND $__timeFilter(s.\"CreationDatetime\") UNION ALL SELECT r.\"NodeId\", $applyRebalanceCosts * COALESCE(r.\"FeePaidSats\", 0) AS cost_sats FROM \"Rebalances\" r WHERE r.\"Status\" = 3 AND r.\"FeePaidSats\" IS NOT NULL AND $__timeFilter(r.\"CreationDatetime\")) yc JOIN \"Nodes\" n ON n.\"Id\" = yc.\"NodeId\" WHERE n.\"PubKey\" IN ($node)), participating_chans AS (SELECT DISTINCT u.chan_id FROM \"ForwardingHtlcEvents\" f CROSS JOIN LATERAL (VALUES (f.\"IncomingChannelId\"), (f.\"OutgoingChannelId\")) AS u(chan_id) WHERE $__timeFilter(f.\"EventTimestamp\") AND f.\"Outcome\" = 1 AND f.\"ManagedNodePubKey\" IN ($node) AND f.\"FeeMsat\" IS NOT NULL UNION SELECT c.\"ChanId\" AS chan_id FROM \"Channels\" c JOIN \"Nodes\" n ON n.\"Id\" IN (c.\"SourceNodeId\", c.\"DestinationNodeId\") WHERE $activeChannelsOnly = 0 AND n.\"PubKey\" IN ($node)), qualifying_liquidity AS (SELECT COALESCE(SUM(c.\"SatsAmount\" * GREATEST(EXTRACT(EPOCH FROM (LEAST(CASE WHEN c.\"Status\" = 2 THEN COALESCE((to_jsonb(c) ->> 'ClosedAt')::timestamptz, c.\"UpdateDatetime\") ELSE b.window_end END, b.window_end) - GREATEST(c.\"CreationDatetime\", b.window_start))) / 86400.0, 0.0) / b.window_days), 0) AS liquidity_sats FROM \"Channels\" c CROSS JOIN bounds b JOIN participating_chans p ON p.chan_id = c.\"ChanId\" WHERE (c.\"CreatedByNodeGuard\" = TRUE OR $liquidityScope = 0)) SELECT CASE WHEN ql.liquidity_sats > 0 THEN ((tf.fee_sats - tyc.yield_cost_sats)::numeric / ql.liquidity_sats) * (365.0 / b.window_days)::numeric ELSE NULL END AS \"APR\" FROM total_fees tf, total_yield_costs tyc, qualifying_liquidity ql, bounds b", "refId": "A" } ], @@ -4313,7 +4313,7 @@ "editorMode": "code", "format": "table", "rawQuery": true, - "rawSql": "WITH bounds AS (SELECT $__timeFrom()::timestamptz AS window_start, $__timeTo()::timestamptz AS window_end), total_fees AS (SELECT COALESCE(ROUND(SUM(\"FeeMsat\") / 1000.0)::bigint, 0) AS fee_sats FROM \"ForwardingHtlcEvents\" WHERE $__timeFilter(\"EventTimestamp\") AND \"Outcome\" = 1 AND \"ManagedNodePubKey\" IN ($node) AND \"FeeMsat\" IS NOT NULL), total_yield_costs AS (SELECT COALESCE(SUM(yc.cost_sats), 0) AS yield_cost_sats FROM (SELECT s.\"NodeId\", $applySwapCosts * (COALESCE(s.\"ServiceFeeSats\", 0) + COALESCE(s.\"LightningFeeSats\", 0) + COALESCE(s.\"OnChainFeeSats\", 0)) AS cost_sats FROM \"SwapOuts\" s WHERE s.\"Status\" = 1 AND $__timeFilter(s.\"CreationDatetime\") UNION ALL SELECT r.\"NodeId\", $applyRebalanceCosts * COALESCE(r.\"FeePaidSats\", 0) AS cost_sats FROM \"Rebalances\" r WHERE r.\"Status\" = 3 AND r.\"FeePaidSats\" IS NOT NULL AND $__timeFilter(r.\"CreationDatetime\")) yc JOIN \"Nodes\" n ON n.\"Id\" = yc.\"NodeId\" WHERE n.\"PubKey\" IN ($node)), participating_chans AS (SELECT DISTINCT u.chan_id FROM \"ForwardingHtlcEvents\" f CROSS JOIN LATERAL (VALUES (f.\"IncomingChannelId\"), (f.\"OutgoingChannelId\")) AS u(chan_id) WHERE $__timeFilter(f.\"EventTimestamp\") AND f.\"Outcome\" = 1 AND f.\"ManagedNodePubKey\" IN ($node) AND f.\"FeeMsat\" IS NOT NULL), qualifying_liquidity AS (SELECT COALESCE(SUM(c.\"SatsAmount\" * GREATEST(EXTRACT(EPOCH FROM (LEAST(CASE WHEN c.\"Status\" = 2 THEN COALESCE((to_jsonb(c) ->> 'ClosedAt')::timestamptz, c.\"UpdateDatetime\") ELSE b.window_end END, b.window_end) - GREATEST(c.\"CreationDatetime\", b.window_start))) / 86400.0, 0.0) / GREATEST(EXTRACT(EPOCH FROM (b.window_end - b.window_start)) / 86400.0, 0.000001)), 0) AS liquidity_sats FROM \"Channels\" c CROSS JOIN bounds b JOIN participating_chans p ON p.chan_id = c.\"ChanId\" WHERE (c.\"CreatedByNodeGuard\" = TRUE OR $liquidityScope = 0)) SELECT CASE WHEN ql.liquidity_sats > 0 THEN ((tf.fee_sats - tyc.yield_cost_sats)::numeric / ql.liquidity_sats) ELSE NULL END AS \"Raw Yield\" FROM total_fees tf, total_yield_costs tyc, qualifying_liquidity ql", + "rawSql": "WITH bounds AS (SELECT $__timeFrom()::timestamptz AS window_start, $__timeTo()::timestamptz AS window_end), total_fees AS (SELECT COALESCE(ROUND(SUM(\"FeeMsat\") / 1000.0)::bigint, 0) AS fee_sats FROM \"ForwardingHtlcEvents\" WHERE $__timeFilter(\"EventTimestamp\") AND \"Outcome\" = 1 AND \"ManagedNodePubKey\" IN ($node) AND \"FeeMsat\" IS NOT NULL), total_yield_costs AS (SELECT COALESCE(SUM(yc.cost_sats), 0) AS yield_cost_sats FROM (SELECT s.\"NodeId\", $applySwapCosts * (COALESCE(s.\"ServiceFeeSats\", 0) + COALESCE(s.\"LightningFeeSats\", 0) + COALESCE(s.\"OnChainFeeSats\", 0)) AS cost_sats FROM \"SwapOuts\" s WHERE s.\"Status\" = 1 AND $__timeFilter(s.\"CreationDatetime\") UNION ALL SELECT r.\"NodeId\", $applyRebalanceCosts * COALESCE(r.\"FeePaidSats\", 0) AS cost_sats FROM \"Rebalances\" r WHERE r.\"Status\" = 3 AND r.\"FeePaidSats\" IS NOT NULL AND $__timeFilter(r.\"CreationDatetime\")) yc JOIN \"Nodes\" n ON n.\"Id\" = yc.\"NodeId\" WHERE n.\"PubKey\" IN ($node)), participating_chans AS (SELECT DISTINCT u.chan_id FROM \"ForwardingHtlcEvents\" f CROSS JOIN LATERAL (VALUES (f.\"IncomingChannelId\"), (f.\"OutgoingChannelId\")) AS u(chan_id) WHERE $__timeFilter(f.\"EventTimestamp\") AND f.\"Outcome\" = 1 AND f.\"ManagedNodePubKey\" IN ($node) AND f.\"FeeMsat\" IS NOT NULL UNION SELECT c.\"ChanId\" AS chan_id FROM \"Channels\" c JOIN \"Nodes\" n ON n.\"Id\" IN (c.\"SourceNodeId\", c.\"DestinationNodeId\") WHERE $activeChannelsOnly = 0 AND n.\"PubKey\" IN ($node)), qualifying_liquidity AS (SELECT COALESCE(SUM(c.\"SatsAmount\" * GREATEST(EXTRACT(EPOCH FROM (LEAST(CASE WHEN c.\"Status\" = 2 THEN COALESCE((to_jsonb(c) ->> 'ClosedAt')::timestamptz, c.\"UpdateDatetime\") ELSE b.window_end END, b.window_end) - GREATEST(c.\"CreationDatetime\", b.window_start))) / 86400.0, 0.0) / GREATEST(EXTRACT(EPOCH FROM (b.window_end - b.window_start)) / 86400.0, 0.000001)), 0) AS liquidity_sats FROM \"Channels\" c CROSS JOIN bounds b JOIN participating_chans p ON p.chan_id = c.\"ChanId\" WHERE (c.\"CreatedByNodeGuard\" = TRUE OR $liquidityScope = 0)) SELECT CASE WHEN ql.liquidity_sats > 0 THEN ((tf.fee_sats - tyc.yield_cost_sats)::numeric / ql.liquidity_sats) ELSE NULL END AS \"Raw Yield\" FROM total_fees tf, total_yield_costs tyc, qualifying_liquidity ql", "refId": "A" } ], @@ -4392,7 +4392,7 @@ "editorMode": "code", "format": "table", "rawQuery": true, - "rawSql": "WITH bounds AS (SELECT $__timeFrom()::timestamptz AS window_start, $__timeTo()::timestamptz AS window_end, GREATEST(EXTRACT(EPOCH FROM ($__timeTo()::timestamptz - $__timeFrom()::timestamptz)) / 86400.0, 0.000001) AS window_days), node_fees AS (SELECT \"ManagedNodePubKey\", MAX(\"ManagedNodeName\") AS managed_node_name, ROUND(SUM(\"FeeMsat\") / 1000.0)::bigint AS fee_sats FROM \"ForwardingHtlcEvents\" WHERE $__timeFilter(\"EventTimestamp\") AND \"Outcome\" = 1 AND \"ManagedNodePubKey\" IN ($node) AND \"FeeMsat\" IS NOT NULL GROUP BY \"ManagedNodePubKey\"), node_yield_costs AS (SELECT n.\"PubKey\" AS managed_node_pubkey, COALESCE(SUM(yc.cost_sats), 0) AS yield_cost_sats FROM (SELECT s.\"NodeId\", $applySwapCosts * (COALESCE(s.\"ServiceFeeSats\", 0) + COALESCE(s.\"LightningFeeSats\", 0) + COALESCE(s.\"OnChainFeeSats\", 0)) AS cost_sats FROM \"SwapOuts\" s WHERE s.\"Status\" = 1 AND $__timeFilter(s.\"CreationDatetime\") UNION ALL SELECT r.\"NodeId\", $applyRebalanceCosts * COALESCE(r.\"FeePaidSats\", 0) AS cost_sats FROM \"Rebalances\" r WHERE r.\"Status\" = 3 AND r.\"FeePaidSats\" IS NOT NULL AND $__timeFilter(r.\"CreationDatetime\")) yc JOIN \"Nodes\" n ON n.\"Id\" = yc.\"NodeId\" WHERE n.\"PubKey\" IN ($node) GROUP BY n.\"PubKey\"), node_participating_chans AS (SELECT DISTINCT f.\"ManagedNodePubKey\", u.chan_id FROM \"ForwardingHtlcEvents\" f CROSS JOIN LATERAL (VALUES (f.\"IncomingChannelId\"), (f.\"OutgoingChannelId\")) AS u(chan_id) WHERE $__timeFilter(f.\"EventTimestamp\") AND f.\"Outcome\" = 1 AND f.\"ManagedNodePubKey\" IN ($node) AND f.\"FeeMsat\" IS NOT NULL), node_liquidity AS (SELECT npc.\"ManagedNodePubKey\", SUM(c.\"SatsAmount\" * GREATEST(EXTRACT(EPOCH FROM (LEAST(CASE WHEN c.\"Status\" = 2 THEN COALESCE((to_jsonb(c) ->> 'ClosedAt')::timestamptz, c.\"UpdateDatetime\") ELSE b.window_end END, b.window_end) - GREATEST(c.\"CreationDatetime\", b.window_start))) / 86400.0, 0.0) / b.window_days) AS liquidity_sats FROM node_participating_chans npc JOIN \"Channels\" c ON c.\"ChanId\" = npc.chan_id CROSS JOIN bounds b WHERE (c.\"CreatedByNodeGuard\" = TRUE OR $liquidityScope = 0) GROUP BY npc.\"ManagedNodePubKey\") SELECT nf.managed_node_name AS \"Node\", CASE WHEN nl.liquidity_sats > 0 THEN ((nf.fee_sats - COALESCE(nyc.yield_cost_sats, 0))::numeric / nl.liquidity_sats) * (365.0 / b.window_days)::numeric ELSE NULL END AS \"APR\" FROM node_fees nf LEFT JOIN node_liquidity nl ON nl.\"ManagedNodePubKey\" = nf.\"ManagedNodePubKey\" LEFT JOIN node_yield_costs nyc ON nyc.managed_node_pubkey = nf.\"ManagedNodePubKey\" CROSS JOIN bounds b ORDER BY \"APR\" DESC NULLS LAST", + "rawSql": "WITH bounds AS (SELECT $__timeFrom()::timestamptz AS window_start, $__timeTo()::timestamptz AS window_end, GREATEST(EXTRACT(EPOCH FROM ($__timeTo()::timestamptz - $__timeFrom()::timestamptz)) / 86400.0, 0.000001) AS window_days), node_fees AS (SELECT \"ManagedNodePubKey\", MAX(\"ManagedNodeName\") AS managed_node_name, ROUND(SUM(\"FeeMsat\") / 1000.0)::bigint AS fee_sats FROM \"ForwardingHtlcEvents\" WHERE $__timeFilter(\"EventTimestamp\") AND \"Outcome\" = 1 AND \"ManagedNodePubKey\" IN ($node) AND \"FeeMsat\" IS NOT NULL GROUP BY \"ManagedNodePubKey\"), node_yield_costs AS (SELECT n.\"PubKey\" AS managed_node_pubkey, COALESCE(SUM(yc.cost_sats), 0) AS yield_cost_sats FROM (SELECT s.\"NodeId\", $applySwapCosts * (COALESCE(s.\"ServiceFeeSats\", 0) + COALESCE(s.\"LightningFeeSats\", 0) + COALESCE(s.\"OnChainFeeSats\", 0)) AS cost_sats FROM \"SwapOuts\" s WHERE s.\"Status\" = 1 AND $__timeFilter(s.\"CreationDatetime\") UNION ALL SELECT r.\"NodeId\", $applyRebalanceCosts * COALESCE(r.\"FeePaidSats\", 0) AS cost_sats FROM \"Rebalances\" r WHERE r.\"Status\" = 3 AND r.\"FeePaidSats\" IS NOT NULL AND $__timeFilter(r.\"CreationDatetime\")) yc JOIN \"Nodes\" n ON n.\"Id\" = yc.\"NodeId\" WHERE n.\"PubKey\" IN ($node) GROUP BY n.\"PubKey\"), node_participating_chans AS (SELECT DISTINCT f.\"ManagedNodePubKey\", u.chan_id FROM \"ForwardingHtlcEvents\" f CROSS JOIN LATERAL (VALUES (f.\"IncomingChannelId\"), (f.\"OutgoingChannelId\")) AS u(chan_id) WHERE $__timeFilter(f.\"EventTimestamp\") AND f.\"Outcome\" = 1 AND f.\"ManagedNodePubKey\" IN ($node) AND f.\"FeeMsat\" IS NOT NULL UNION SELECT n.\"PubKey\" AS \"ManagedNodePubKey\", c.\"ChanId\" AS chan_id FROM \"Channels\" c JOIN \"Nodes\" n ON n.\"Id\" IN (c.\"SourceNodeId\", c.\"DestinationNodeId\") WHERE $activeChannelsOnly = 0 AND n.\"PubKey\" IN ($node)), node_liquidity AS (SELECT npc.\"ManagedNodePubKey\", SUM(c.\"SatsAmount\" * GREATEST(EXTRACT(EPOCH FROM (LEAST(CASE WHEN c.\"Status\" = 2 THEN COALESCE((to_jsonb(c) ->> 'ClosedAt')::timestamptz, c.\"UpdateDatetime\") ELSE b.window_end END, b.window_end) - GREATEST(c.\"CreationDatetime\", b.window_start))) / 86400.0, 0.0) / b.window_days) AS liquidity_sats FROM node_participating_chans npc JOIN \"Channels\" c ON c.\"ChanId\" = npc.chan_id CROSS JOIN bounds b WHERE (c.\"CreatedByNodeGuard\" = TRUE OR $liquidityScope = 0) GROUP BY npc.\"ManagedNodePubKey\") SELECT nf.managed_node_name AS \"Node\", CASE WHEN nl.liquidity_sats > 0 THEN ((nf.fee_sats - COALESCE(nyc.yield_cost_sats, 0))::numeric / nl.liquidity_sats) * (365.0 / b.window_days)::numeric ELSE NULL END AS \"APR\" FROM node_fees nf LEFT JOIN node_liquidity nl ON nl.\"ManagedNodePubKey\" = nf.\"ManagedNodePubKey\" LEFT JOIN node_yield_costs nyc ON nyc.managed_node_pubkey = nf.\"ManagedNodePubKey\" CROSS JOIN bounds b ORDER BY \"APR\" DESC NULLS LAST", "refId": "A" } ], @@ -5225,7 +5225,7 @@ "editorMode": "code", "format": "time_series", "rawQuery": true, - "rawSql": "WITH days AS (SELECT generate_series(date_trunc('hour', $__timeFrom()::timestamptz), $__timeTo()::timestamptz, INTERVAL '1 hour') AS bucket_start), day_buckets AS (SELECT bucket_start, bucket_start + INTERVAL '1 hour' AS bucket_end FROM days), nodes AS (SELECT \"ManagedNodePubKey\" AS pubkey, MAX(\"ManagedNodeName\") AS node_name FROM \"ForwardingHtlcEvents\" WHERE $__timeFilter(\"EventTimestamp\") AND \"ManagedNodePubKey\" IN ($node) GROUP BY 1), participating_node_chans AS (SELECT DISTINCT f.\"ManagedNodePubKey\" AS pubkey, u.chan_id FROM \"ForwardingHtlcEvents\" f CROSS JOIN LATERAL (VALUES (f.\"IncomingChannelId\"), (f.\"OutgoingChannelId\")) u(chan_id) WHERE $__timeFilter(f.\"EventTimestamp\") AND f.\"Outcome\" = 1 AND f.\"FeeMsat\" IS NOT NULL AND f.\"ManagedNodePubKey\" IN ($node)), day_node_grid AS (SELECT db.bucket_start, db.bucket_end, n.pubkey, n.node_name FROM day_buckets db CROSS JOIN nodes n), day_fees AS (SELECT g.bucket_start, g.pubkey, g.node_name, COALESCE(ROUND(SUM(f.\"FeeMsat\") / 1000.0)::bigint, 0) AS fee_sats FROM day_node_grid g LEFT JOIN \"ForwardingHtlcEvents\" f ON f.\"EventTimestamp\" >= g.bucket_start AND f.\"EventTimestamp\" < g.bucket_end AND f.\"Outcome\" = 1 AND f.\"FeeMsat\" IS NOT NULL AND f.\"ManagedNodePubKey\" = g.pubkey GROUP BY g.bucket_start, g.pubkey, g.node_name), day_yield_costs AS (SELECT g.bucket_start, g.pubkey, COALESCE(SUM(yc.cost_sats), 0) AS yield_cost_sats FROM day_node_grid g LEFT JOIN \"Nodes\" n ON n.\"PubKey\" = g.pubkey LEFT JOIN (SELECT s.\"NodeId\", s.\"CreationDatetime\" AS cost_time, $applySwapCosts * (COALESCE(s.\"ServiceFeeSats\", 0) + COALESCE(s.\"LightningFeeSats\", 0) + COALESCE(s.\"OnChainFeeSats\", 0)) AS cost_sats FROM \"SwapOuts\" s WHERE s.\"Status\" = 1 AND $__timeFilter(s.\"CreationDatetime\") UNION ALL SELECT r.\"NodeId\", r.\"CreationDatetime\" AS cost_time, $applyRebalanceCosts * COALESCE(r.\"FeePaidSats\", 0) AS cost_sats FROM \"Rebalances\" r WHERE r.\"Status\" = 3 AND r.\"FeePaidSats\" IS NOT NULL AND $__timeFilter(r.\"CreationDatetime\")) yc ON yc.\"NodeId\" = n.\"Id\" AND yc.cost_time >= g.bucket_start AND yc.cost_time < g.bucket_end GROUP BY g.bucket_start, g.pubkey), day_liquidity AS (SELECT g.bucket_start, g.pubkey, COALESCE(SUM(c.\"SatsAmount\" * GREATEST(EXTRACT(EPOCH FROM (LEAST(CASE WHEN c.\"Status\" = 2 THEN COALESCE((to_jsonb(c) ->> 'ClosedAt')::timestamptz, c.\"UpdateDatetime\") ELSE g.bucket_end END, g.bucket_end) - GREATEST(c.\"CreationDatetime\", g.bucket_start))) / 86400.0, 0.0)), 0) AS sat_days FROM day_node_grid g LEFT JOIN participating_node_chans pc ON pc.pubkey = g.pubkey LEFT JOIN \"Channels\" c ON c.\"ChanId\" = pc.chan_id AND (c.\"CreatedByNodeGuard\" = TRUE OR $liquidityScope = 0) GROUP BY g.bucket_start, g.pubkey), rolling AS (SELECT df.bucket_start, df.pubkey, df.node_name, SUM(df.fee_sats) OVER w AS r_fee, SUM(COALESCE(dyc.yield_cost_sats, 0)) OVER w AS r_cost, SUM(COALESCE(dl.sat_days, 0)) OVER w AS r_satdays, COUNT(*) OVER w AS r_count FROM day_fees df LEFT JOIN day_yield_costs dyc ON dyc.bucket_start = df.bucket_start AND dyc.pubkey = df.pubkey LEFT JOIN day_liquidity dl ON dl.bucket_start = df.bucket_start AND dl.pubkey = df.pubkey WINDOW w AS (PARTITION BY df.pubkey ORDER BY df.bucket_start ROWS BETWEEN (CASE '$aprWindow' WHEN 'day' THEN 23 WHEN 'week' THEN 167 ELSE 719 END) PRECEDING AND CURRENT ROW)) SELECT t AS \"time\", m AS metric, v AS value FROM (SELECT bucket_start AS t, node_name || ' APR' AS m, CASE WHEN r_satdays > 0 THEN (r_fee - r_cost)::numeric * 365.0 / r_satdays ELSE NULL END AS v FROM rolling UNION ALL SELECT bucket_start AS t, node_name || ' Liquidity (BTC)' AS m, CASE WHEN r_count > 0 THEN (r_satdays * 24.0 / r_count / 100000000.0)::numeric(20,8) ELSE NULL END AS v FROM rolling) sub ORDER BY t, m", + "rawSql": "WITH days AS (SELECT generate_series(date_trunc('hour', $__timeFrom()::timestamptz), $__timeTo()::timestamptz, INTERVAL '1 hour') AS bucket_start), day_buckets AS (SELECT bucket_start, bucket_start + INTERVAL '1 hour' AS bucket_end FROM days), nodes AS (SELECT \"ManagedNodePubKey\" AS pubkey, MAX(\"ManagedNodeName\") AS node_name FROM \"ForwardingHtlcEvents\" WHERE $__timeFilter(\"EventTimestamp\") AND \"ManagedNodePubKey\" IN ($node) GROUP BY 1), participating_node_chans AS (SELECT DISTINCT f.\"ManagedNodePubKey\" AS pubkey, u.chan_id FROM \"ForwardingHtlcEvents\" f CROSS JOIN LATERAL (VALUES (f.\"IncomingChannelId\"), (f.\"OutgoingChannelId\")) u(chan_id) WHERE $__timeFilter(f.\"EventTimestamp\") AND f.\"Outcome\" = 1 AND f.\"FeeMsat\" IS NOT NULL AND f.\"ManagedNodePubKey\" IN ($node) UNION SELECT n.\"PubKey\" AS pubkey, c.\"ChanId\" AS chan_id FROM \"Channels\" c JOIN \"Nodes\" n ON n.\"Id\" IN (c.\"SourceNodeId\", c.\"DestinationNodeId\") WHERE $activeChannelsOnly = 0 AND n.\"PubKey\" IN ($node)), day_node_grid AS (SELECT db.bucket_start, db.bucket_end, n.pubkey, n.node_name FROM day_buckets db CROSS JOIN nodes n), day_fees AS (SELECT g.bucket_start, g.pubkey, g.node_name, COALESCE(ROUND(SUM(f.\"FeeMsat\") / 1000.0)::bigint, 0) AS fee_sats FROM day_node_grid g LEFT JOIN \"ForwardingHtlcEvents\" f ON f.\"EventTimestamp\" >= g.bucket_start AND f.\"EventTimestamp\" < g.bucket_end AND f.\"Outcome\" = 1 AND f.\"FeeMsat\" IS NOT NULL AND f.\"ManagedNodePubKey\" = g.pubkey GROUP BY g.bucket_start, g.pubkey, g.node_name), day_yield_costs AS (SELECT g.bucket_start, g.pubkey, COALESCE(SUM(yc.cost_sats), 0) AS yield_cost_sats FROM day_node_grid g LEFT JOIN \"Nodes\" n ON n.\"PubKey\" = g.pubkey LEFT JOIN (SELECT s.\"NodeId\", s.\"CreationDatetime\" AS cost_time, $applySwapCosts * (COALESCE(s.\"ServiceFeeSats\", 0) + COALESCE(s.\"LightningFeeSats\", 0) + COALESCE(s.\"OnChainFeeSats\", 0)) AS cost_sats FROM \"SwapOuts\" s WHERE s.\"Status\" = 1 AND $__timeFilter(s.\"CreationDatetime\") UNION ALL SELECT r.\"NodeId\", r.\"CreationDatetime\" AS cost_time, $applyRebalanceCosts * COALESCE(r.\"FeePaidSats\", 0) AS cost_sats FROM \"Rebalances\" r WHERE r.\"Status\" = 3 AND r.\"FeePaidSats\" IS NOT NULL AND $__timeFilter(r.\"CreationDatetime\")) yc ON yc.\"NodeId\" = n.\"Id\" AND yc.cost_time >= g.bucket_start AND yc.cost_time < g.bucket_end GROUP BY g.bucket_start, g.pubkey), day_liquidity AS (SELECT g.bucket_start, g.pubkey, COALESCE(SUM(c.\"SatsAmount\" * GREATEST(EXTRACT(EPOCH FROM (LEAST(CASE WHEN c.\"Status\" = 2 THEN COALESCE((to_jsonb(c) ->> 'ClosedAt')::timestamptz, c.\"UpdateDatetime\") ELSE g.bucket_end END, g.bucket_end) - GREATEST(c.\"CreationDatetime\", g.bucket_start))) / 86400.0, 0.0)), 0) AS sat_days FROM day_node_grid g LEFT JOIN participating_node_chans pc ON pc.pubkey = g.pubkey LEFT JOIN \"Channels\" c ON c.\"ChanId\" = pc.chan_id AND (c.\"CreatedByNodeGuard\" = TRUE OR $liquidityScope = 0) GROUP BY g.bucket_start, g.pubkey), rolling AS (SELECT df.bucket_start, df.pubkey, df.node_name, SUM(df.fee_sats) OVER w AS r_fee, SUM(COALESCE(dyc.yield_cost_sats, 0)) OVER w AS r_cost, SUM(COALESCE(dl.sat_days, 0)) OVER w AS r_satdays, COUNT(*) OVER w AS r_count FROM day_fees df LEFT JOIN day_yield_costs dyc ON dyc.bucket_start = df.bucket_start AND dyc.pubkey = df.pubkey LEFT JOIN day_liquidity dl ON dl.bucket_start = df.bucket_start AND dl.pubkey = df.pubkey WINDOW w AS (PARTITION BY df.pubkey ORDER BY df.bucket_start ROWS BETWEEN (CASE '$aprWindow' WHEN 'day' THEN 23 WHEN 'week' THEN 167 ELSE 719 END) PRECEDING AND CURRENT ROW)) SELECT t AS \"time\", m AS metric, v AS value FROM (SELECT bucket_start AS t, node_name || ' APR' AS m, CASE WHEN r_satdays > 0 THEN (r_fee - r_cost)::numeric * 365.0 / r_satdays ELSE NULL END AS v FROM rolling UNION ALL SELECT bucket_start AS t, node_name || ' Liquidity (BTC)' AS m, CASE WHEN r_count > 0 THEN (r_satdays * 24.0 / r_count / 100000000.0)::numeric(20,8) ELSE NULL END AS v FROM rolling) sub ORDER BY t, m", "refId": "A" } ], @@ -5429,6 +5429,35 @@ "skipUrlSync": false, "type": "custom" }, + { + "current": { + "selected": true, + "text": "Yes", + "value": "1" + }, + "description": "When set to Yes, aggregate APR denominators only include channels that settled at least one HTLC in the selected window (\"active\" channels). When No, all NodeGuard-managed channels for the selected nodes contribute liquidity, regardless of whether they routed.", + "hide": 0, + "includeAll": false, + "label": "Active Channels Only", + "multi": false, + "name": "activeChannelsOnly", + "options": [ + { + "selected": true, + "text": "Yes", + "value": "1" + }, + { + "selected": false, + "text": "No", + "value": "0" + } + ], + "query": "Yes : 1, No : 0", + "queryValue": "", + "skipUrlSync": false, + "type": "custom" + }, { "current": { "selected": true,