Skip to content

Instantly share code, notes, and snippets.

@arussellsaw
Last active December 26, 2023 21:47
Show Gist options
  • Star 6 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save arussellsaw/2bd994484ce107c6727558913e060742 to your computer and use it in GitHub Desktop.
Save arussellsaw/2bd994484ce107c6727558913e060742 to your computer and use it in GitHub Desktop.
Monzo Plus Grafana Dashboard

Monzo Plus Grafana Dashboard

Now that Monzo Plus is out, i thought i'd share how i've set up my personal budgeting dashboards using the Google Sheets export that i showed off in this tweet: https://twitter.com/arussellsaw/status/1283876210750230530?s=20

A quick note on cost, bigquery isn't free, but even with my Monzo transaction history, which i've had since 2016 (although the google sheets only goes as far as the prepaid->current account migration) the volume of data here is still so low that bigquery doesn't even appear on my GCP billing, as we're well below the 1tb billing threshold. That being said if you somehow misconfigure things, it's possible that you could incur billing costs, so it's worth being wary of that.

The sheets export automatically creates and updates a google sheet with each transaction, and luckily google BigQuery supports using a google sheet as an external table, so we can just query the sheet directly, and have it updated as new rows are added to the sheet.

Creating the external table

First we create a new bigquery table that looks like this, with the link to your monzo transactions sheet:

don't forget to add the 'Monzo Transactions' sheet range, and to skip the first header row in the advanced options!

and use this here as your schema:

transaction_id:STRING,
date:STRING,
time:STRING,
type:STRING,
name:STRING,
emoji:STRING,
category:STRING,
amount:NUMERIC,
currency:STRING,
local_amount:NUMERIC,
local_currency:STRING,
notes:STRING,
address:STRING,
receipt:STRING,
description:STRING,
category_split:STRING

Now we have a table, we should add a running balance to it to make building graphs a bit easier. In order to do this we'll add a scheduled query that sums the amount of all transactions sequentially, and attaches it as an additional column. We'll also reformat the timestamp to be a little more SQL friendly.

I have this query set up to run hourly, with the output being sent into a new table called monzo_transactions_hourly

WITH base AS (
  SELECT
    * 
  FROM 
    `russellsaw.personal_data.monzo_transactions`
  WHERE transaction_id IS NOT NULL
),
timestamped AS (
  SELECT
    *,
    PARSE_TIMESTAMP("%d/%m/%Y %T",  CONCAT(date, " ", time)) as timestamp
  FROM
    base
),
balanced AS (
  SELECT
   *,
   SUM(amount) OVER tx_date as balance
  FROM
    timestamped
  WINDOW tx_date AS (ORDER BY timestamp ASC ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
)
SELECT * FROM balanced

Now that we have a table with an hourly balance we can start to build a dashboard! if you don't have a grafana instance already, head to https://grafana.com/grafana/download to get started, i recommend docker but it does make installing plugins harder, and you'll need to do that for the next step:

Next you'll want to install this extension here: https://grafana.com/grafana/plugins/doitintl-bigquery-datasource this is a datasource plugin for bigquery that allows us to directly query bigquery and build panels in grafana, super cool.

You'll need to create a service account and keyfile, i'd recommend creating a new IAM user that is only allowed to query bigquery, and then using that as the credentials for grafana, to prevent a compromise of your grafana instance leading to a compromise of your entire GCP account.

If you import the dashboard JSON below you should get a version of my dashboard, but the tables in the query will all point to my personal project, so you'll want to update the queries to use your table name. also some of the panels have specific references to my pots, so you'll want to make some edits for your own data.

And that should be done! if you have any issues @ me on twitter @arussellsaw and i'll see if i can help.

{
"__inputs": [
{
"name": "DS_GOOGLE_BIGQUERY",
"label": "Google BigQuery",
"description": "",
"type": "datasource",
"pluginId": "doitintl-bigquery-datasource",
"pluginName": "Google BigQuery"
}
],
"__requires": [
{
"type": "datasource",
"id": "doitintl-bigquery-datasource",
"name": "Google BigQuery",
"version": "1.0.7"
},
{
"type": "grafana",
"id": "grafana",
"name": "Grafana",
"version": "7.0.3"
},
{
"type": "panel",
"id": "graph",
"name": "Graph",
"version": ""
},
{
"type": "panel",
"id": "stat",
"name": "Stat",
"version": ""
},
{
"type": "panel",
"id": "table-old",
"name": "Table (old)",
"version": ""
}
],
"annotations": {
"list": [
{
"builtIn": 1,
"datasource": "-- Grafana --",
"enable": true,
"hide": true,
"iconColor": "rgba(0, 211, 255, 1)",
"name": "Annotations & Alerts",
"type": "dashboard"
}
]
},
"editable": true,
"gnetId": null,
"graphTooltip": 1,
"id": null,
"links": [],
"panels": [
{
"cacheTimeout": null,
"datasource": "${DS_GOOGLE_BIGQUERY}",
"fieldConfig": {
"defaults": {
"custom": {},
"displayName": "",
"mappings": [],
"nullValueMode": "connected",
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "semi-dark-red",
"value": null
},
{
"color": "#EAB839",
"value": 1000
},
{
"color": "light-green",
"value": 1500
},
{
"color": "super-light-green",
"value": 2000
}
]
},
"unit": "currencyGBP"
},
"overrides": []
},
"gridPos": {
"h": 4,
"w": 4,
"x": 0,
"y": 0
},
"id": 8,
"links": [],
"options": {
"colorMode": "value",
"graphMode": "none",
"justifyMode": "auto",
"orientation": "horizontal",
"reduceOptions": {
"calcs": [
"lastNotNull"
],
"fields": "",
"values": false
}
},
"pluginVersion": "7.0.3",
"targets": [
{
"format": "time_series",
"group": [],
"metricColumn": "none",
"orderByCol": "1",
"orderBySort": "1",
"rawQuery": true,
"rawSql": "SELECT\n balance,\n timestamp\nFROM\n `russellsaw.personal_data.monzo_transactions_hourly`\nWHERE\n $__timeFilter(timestamp)\nORDER BY timestamp DESC\nlimit 1\n",
"refId": "A",
"select": [
[
{
"params": [
"-- value --"
],
"type": "column"
}
]
],
"timeColumn": "-- time --",
"timeColumnType": "TIMESTAMP",
"where": [
{
"name": "$__timeFilter",
"params": [],
"type": "macro"
}
]
}
],
"timeFrom": null,
"timeShift": null,
"title": "",
"transparent": true,
"type": "stat"
},
{
"aliasColors": {
"balance": "super-light-green"
},
"bars": true,
"dashLength": 10,
"dashes": false,
"datasource": "${DS_GOOGLE_BIGQUERY}",
"fieldConfig": {
"defaults": {
"custom": {}
},
"overrides": []
},
"fill": 1,
"fillGradient": 0,
"gridPos": {
"h": 7,
"w": 13,
"x": 4,
"y": 0
},
"hiddenSeries": false,
"id": 2,
"legend": {
"avg": false,
"current": false,
"max": false,
"min": false,
"show": true,
"total": false,
"values": false
},
"lines": false,
"linewidth": 1,
"maxDataPoints": 1000000,
"nullPointMode": "null",
"options": {
"dataLinks": []
},
"percentage": false,
"pointradius": 2,
"points": false,
"renderer": "flot",
"seriesOverrides": [
{
"$$hashKey": "object:673",
"alias": "average",
"bars": false,
"color": "#8AB8FF",
"lines": true,
"linewidth": 3,
"pointradius": 3,
"points": true
}
],
"spaceLength": 10,
"stack": false,
"steppedLine": false,
"targets": [
{
"dataset": "personal_data",
"format": "time_series",
"group": [],
"metricColumn": "none",
"orderByCol": "1",
"orderBySort": "1",
"partitioned": false,
"partitionedField": "",
"project": "russellsaw",
"rawQuery": true,
"rawSql": "SELECT\n TIMESTAMP_TRUNC(timestamp, DAY) as day,\n min(balance) as balance,\nFROM\n `russellsaw.personal_data.monzo_transactions_hourly`\nWHERE\n $__timeFilter(timestamp)\nGROUP BY day\nORDER BY day ASC",
"refId": "A",
"select": [
[
{
"params": [
"balance"
],
"type": "column"
},
{
"params": [
"avg"
],
"type": "aggregate"
},
{
"params": [
"balance"
],
"type": "alias"
}
],
[
{
"params": [
"balance"
],
"type": "column"
},
{
"params": [
"avg"
],
"type": "aggregate"
},
{
"params": [
"balance"
],
"type": "alias"
}
]
],
"sharded": false,
"table": "monzo_transactions_hourly",
"timeColumn": "timestamp",
"timeColumnType": "TIMESTAMP",
"where": [
{
"name": "$__timeFilter",
"params": [],
"type": "macro"
}
]
},
{
"format": "time_series",
"group": [],
"metricColumn": "none",
"orderByCol": "1",
"orderBySort": "1",
"rawQuery": true,
"rawSql": "WITH averages AS (\nSELECT \n EXTRACT(DAY FROM DATE(timestamp)) as day,\n avg(balance) as balance\nFROM `russellsaw.personal_data.monzo_transactions_hourly`\nGROUP BY day\nORDER BY day ASC\n),\ndays AS (\nSELECT\n TIMESTAMP_TRUNC(timestamp, DAY) as timestamp,\nFROM `russellsaw.personal_data.monzo_transactions_hourly`\nWHERE $__timeFilter(timestamp)\nGROUP BY timestamp\n)\nSELECT \n timestamp,\n balance as average\nFROM \n days\nLEFT JOIN averages ON EXTRACT(DAY FROM DATE(timestamp)) = averages.day\nORDER BY timestamp ASC",
"refId": "B",
"select": [
[
{
"params": [
"-- value --"
],
"type": "column"
}
]
],
"timeColumn": "-- time --",
"timeColumnType": "TIMESTAMP",
"where": [
{
"name": "$__timeFilter",
"params": [],
"type": "macro"
}
]
}
],
"thresholds": [],
"timeFrom": null,
"timeRegions": [],
"timeShift": null,
"title": "Balance",
"tooltip": {
"shared": true,
"sort": 0,
"value_type": "individual"
},
"transparent": true,
"type": "graph",
"xaxis": {
"buckets": null,
"mode": "time",
"name": null,
"show": true,
"values": []
},
"yaxes": [
{
"$$hashKey": "object:690",
"format": "currencyGBP",
"label": "",
"logBase": 1,
"max": null,
"min": null,
"show": true
},
{
"$$hashKey": "object:691",
"format": "short",
"label": null,
"logBase": 1,
"max": null,
"min": null,
"show": true
}
],
"yaxis": {
"align": false,
"alignLevel": null
}
},
{
"columns": [],
"datasource": "${DS_GOOGLE_BIGQUERY}",
"fieldConfig": {
"defaults": {
"custom": {}
},
"overrides": []
},
"fontSize": "100%",
"gridPos": {
"h": 14,
"w": 7,
"x": 17,
"y": 0
},
"id": 10,
"maxDataPoints": 10000000,
"pageSize": null,
"showHeader": true,
"sort": {
"col": null,
"desc": false
},
"styles": [
{
"$$hashKey": "object:103",
"alias": "",
"align": "auto",
"colorMode": null,
"colors": [
"rgba(245, 54, 54, 0.9)",
"rgba(237, 129, 40, 0.89)",
"rgba(50, 172, 45, 0.97)"
],
"dateFormat": "YYYY-MM-DD HH:mm:ss",
"decimals": 2,
"mappingType": 1,
"pattern": "timestamp",
"thresholds": [],
"type": "date",
"unit": "short"
},
{
"$$hashKey": "object:104",
"alias": "",
"align": "auto",
"colorMode": null,
"colors": [
"rgba(245, 54, 54, 0.9)",
"rgba(237, 129, 40, 0.89)",
"rgba(50, 172, 45, 0.97)"
],
"dateFormat": "YYYY-MM-DD HH:mm:ss",
"decimals": 2,
"mappingType": 1,
"pattern": "amount",
"thresholds": [],
"type": "number",
"unit": "currencyGBP"
}
],
"targets": [
{
"format": "table",
"group": [],
"metricColumn": "none",
"orderByCol": "1",
"orderBySort": "1",
"rawQuery": true,
"rawSql": "SELECT\n timestamp,\n name,\n amount * -1 as amount\nFROM\n `russellsaw.personal_data.monzo_transactions_hourly`\nWHERE\n $__timeFilter(timestamp)\n ORDER BY timestamp DESC",
"refId": "A",
"select": [
[
{
"params": [
"-- value --"
],
"type": "column"
}
]
],
"timeColumn": "-- time --",
"timeColumnType": "TIMESTAMP",
"where": [
{
"name": "$__timeFilter",
"params": [],
"type": "macro"
}
]
}
],
"timeFrom": null,
"timeShift": null,
"title": "Transactions",
"transform": "table",
"transparent": true,
"type": "table-old"
},
{
"cacheTimeout": null,
"datasource": "${DS_GOOGLE_BIGQUERY}",
"fieldConfig": {
"defaults": {
"custom": {},
"displayName": "",
"mappings": [],
"nullValueMode": "connected",
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "semi-dark-red",
"value": null
},
{
"color": "#EAB839",
"value": 1000
},
{
"color": "light-green",
"value": 1500
},
{
"color": "super-light-green",
"value": 2000
}
]
},
"unit": "currencyGBP"
},
"overrides": []
},
"gridPos": {
"h": 3,
"w": 4,
"x": 0,
"y": 4
},
"id": 18,
"links": [],
"options": {
"colorMode": "value",
"graphMode": "none",
"justifyMode": "auto",
"orientation": "horizontal",
"reduceOptions": {
"calcs": [
"lastNotNull"
],
"fields": "",
"values": false
}
},
"pluginVersion": "7.0.3",
"targets": [
{
"format": "time_series",
"group": [],
"metricColumn": "none",
"orderByCol": "1",
"orderBySort": "1",
"rawQuery": true,
"rawSql": "WITH base AS (\nSELECT\n timestamp,\n sum(amount) OVER (ORDER BY timestamp ASC ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) * -1 as potbalance,\n balance\nFROM\n `russellsaw.personal_data.monzo_transactions_hourly`\nWHERE\n name LIKE (\"% Pot\")\n)\n\nSELECT\n balance+potbalance,\n timestamp\nFROM\n base\nWHERE\n $__timeFilter(timestamp)\nORDER BY timestamp DESC\nlimit 1\n",
"refId": "A",
"select": [
[
{
"params": [
"-- value --"
],
"type": "column"
}
]
],
"timeColumn": "-- time --",
"timeColumnType": "TIMESTAMP",
"where": [
{
"name": "$__timeFilter",
"params": [],
"type": "macro"
}
]
}
],
"timeFrom": null,
"timeShift": null,
"title": "",
"transparent": true,
"type": "stat"
},
{
"aliasColors": {},
"bars": true,
"dashLength": 10,
"dashes": false,
"datasource": "${DS_GOOGLE_BIGQUERY}",
"fieldConfig": {
"defaults": {
"custom": {}
},
"overrides": []
},
"fill": 1,
"fillGradient": 0,
"gridPos": {
"h": 7,
"w": 17,
"x": 0,
"y": 7
},
"hiddenSeries": false,
"id": 6,
"legend": {
"alignAsTable": true,
"avg": true,
"current": false,
"max": false,
"min": false,
"rightSide": true,
"show": false,
"total": true,
"values": true
},
"lines": false,
"linewidth": 1,
"maxDataPoints": "10000000",
"nullPointMode": "null as zero",
"options": {
"dataLinks": []
},
"percentage": false,
"pointradius": 2,
"points": false,
"renderer": "flot",
"seriesOverrides": [],
"spaceLength": 10,
"stack": true,
"steppedLine": false,
"targets": [
{
"convertToUTC": true,
"dataset": "personal_data",
"format": "time_series",
"group": [
{
"params": [
"1h",
"none"
],
"type": "time"
}
],
"metricColumn": "COALESCE(category, \"empty\")",
"orderByCol": "1",
"orderBySort": "1",
"partitioned": false,
"partitionedField": "",
"project": "russellsaw",
"rawQuery": true,
"rawSql": "WITH categories AS (\n\tSELECT DISTINCT\n\tcategory\n\tFROM `russellsaw.personal_data.monzo_transactions_hourly`\n),\ndates_categories_grid AS (\n\tSELECT\n\tcategory,\n\tdate\n\tFROM categories\n\tCROSS JOIN UNNEST(GENERATE_DATE_ARRAY(DATE_SUB(CURRENT_DATE(), INTERVAL 1000 DAY), CURRENT_DATE())) AS date\n),\ntx AS (\n SELECT \n * \n FROM `russellsaw.personal_data.monzo_transactions_hourly`\n WHERE\n amount < 0\n AND type = \"Card payment\"\n AND $__timeFilter(timestamp)\n)\n\tSELECT\n\t CAST(grid.date AS TIMESTAMP),\n\t COALESCE(grid.category, \"empty\") as metric,\n SUM(COALESCE(tx.amount, 0)) * -1 AS amount\n\tFROM dates_categories_grid grid\n\tLEFT JOIN tx\n ON DATE(tx.timestamp) = grid.date\n AND tx.category = grid.category\n GROUP BY 1,2\n ORDER BY 1,2",
"refId": "A",
"select": [
[
{
"params": [
"amount"
],
"type": "column"
},
{
"params": [
"sum"
],
"type": "aggregate"
},
{
"params": [
"amount"
],
"type": "alias"
}
]
],
"sharded": false,
"table": "monzo_transactions_hourly",
"timeColumn": "timestamp",
"timeColumnType": "TIMESTAMP",
"where": [
{
"name": "$__timeFilter",
"params": [],
"type": "macro"
}
]
}
],
"thresholds": [],
"timeFrom": null,
"timeRegions": [],
"timeShift": null,
"title": "Daily spend by category",
"tooltip": {
"shared": false,
"sort": 2,
"value_type": "individual"
},
"transparent": true,
"type": "graph",
"xaxis": {
"buckets": null,
"mode": "time",
"name": null,
"show": true,
"values": []
},
"yaxes": [
{
"$$hashKey": "object:834",
"format": "currencyGBP",
"label": null,
"logBase": 1,
"max": null,
"min": "0",
"show": true
},
{
"$$hashKey": "object:835",
"format": "short",
"label": null,
"logBase": 1,
"max": null,
"min": null,
"show": true
}
],
"yaxis": {
"align": false,
"alignLevel": null
}
},
{
"aliasColors": {
"🚲 Pot balance": "light-yellow",
"🥑 Pot balance": "light-red",
"🦄 Pot balance": "light-purple"
},
"bars": false,
"dashLength": 10,
"dashes": false,
"datasource": "${DS_GOOGLE_BIGQUERY}",
"fieldConfig": {
"defaults": {
"custom": {}
},
"overrides": []
},
"fill": 1,
"fillGradient": 0,
"gridPos": {
"h": 6,
"w": 12,
"x": 0,
"y": 14
},
"hiddenSeries": false,
"id": 12,
"legend": {
"alignAsTable": true,
"avg": false,
"current": true,
"max": false,
"min": false,
"rightSide": true,
"show": true,
"total": false,
"values": true
},
"lines": true,
"linewidth": 2,
"nullPointMode": "null as zero",
"options": {
"dataLinks": []
},
"percentage": false,
"pointradius": 2,
"points": true,
"renderer": "flot",
"seriesOverrides": [],
"spaceLength": 10,
"stack": false,
"steppedLine": false,
"targets": [
{
"format": "time_series",
"group": [],
"metricColumn": "none",
"orderByCol": "1",
"orderBySort": "1",
"rawQuery": true,
"rawSql": "WITH base AS (\nSELECT\n timestamp,\n \"🥦 Pot\" as metric,\n sum(amount) OVER (ORDER BY timestamp ASC ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) * -1 as balance,\nFROM\n `russellsaw.personal_data.monzo_transactions_hourly`\nWHERE\n name IN (\"🦄 Pot\", \"🥑 Pot\", \"🥦 Pot\")\n)\nSELECT \n TIMESTAMP_TRUNC(timestamp, WEEK) as day,\n metric,\n min(balance) as balance\nFROM base\nGROUP BY day, metric",
"refId": "A",
"select": [
[
{
"params": [
"-- value --"
],
"type": "column"
}
]
],
"timeColumn": "-- time --",
"timeColumnType": "TIMESTAMP",
"where": [
{
"name": "$__timeFilter",
"params": [],
"type": "macro"
}
]
},
{
"format": "time_series",
"group": [],
"metricColumn": "none",
"orderByCol": "1",
"orderBySort": "1",
"rawQuery": true,
"rawSql": "WITH base AS (\nSELECT\n timestamp,\n name as metric,\n sum(amount) OVER (ORDER BY timestamp ASC ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) * -1 as balance,\nFROM\n `russellsaw.personal_data.monzo_transactions_hourly`\nWHERE\n name = \"🚲 Pot\"\n )\nSELECT \n TIMESTAMP_TRUNC(timestamp, WEEK) as day,\n metric,\n max(balance) as balance\nFROM base\nGROUP BY day, metric",
"refId": "B",
"select": [
[
{
"params": [
"-- value --"
],
"type": "column"
}
]
],
"timeColumn": "-- time --",
"timeColumnType": "TIMESTAMP",
"where": [
{
"name": "$__timeFilter",
"params": [],
"type": "macro"
}
]
},
{
"format": "time_series",
"group": [],
"metricColumn": "none",
"orderByCol": "1",
"orderBySort": "1",
"rawQuery": true,
"rawSql": "WITH base AS (\nSELECT\n timestamp,\n name as metric,\n sum(amount) OVER (ORDER BY timestamp ASC ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) * -1 as balance,\nFROM\n `russellsaw.personal_data.monzo_transactions_hourly`\nWHERE\n name = \"🚽 Pot\"\n )\nSELECT \n TIMESTAMP_TRUNC(timestamp, WEEK) as day,\n metric,\n max(balance) as balance\nFROM base\nGROUP BY day, metric",
"refId": "C",
"select": [
[
{
"params": [
"-- value --"
],
"type": "column"
}
]
],
"timeColumn": "-- time --",
"timeColumnType": "TIMESTAMP",
"where": [
{
"name": "$__timeFilter",
"params": [],
"type": "macro"
}
]
}
],
"thresholds": [],
"timeFrom": "365d",
"timeRegions": [],
"timeShift": null,
"title": "Pot Balance",
"tooltip": {
"shared": true,
"sort": 0,
"value_type": "individual"
},
"transparent": true,
"type": "graph",
"xaxis": {
"buckets": null,
"mode": "time",
"name": null,
"show": true,
"values": []
},
"yaxes": [
{
"$$hashKey": "object:93",
"format": "currencyGBP",
"label": null,
"logBase": 1,
"max": null,
"min": "0",
"show": true
},
{
"$$hashKey": "object:94",
"format": "short",
"label": null,
"logBase": 1,
"max": null,
"min": null,
"show": true
}
],
"yaxis": {
"align": false,
"alignLevel": null
}
},
{
"aliasColors": {
"balance": "light-purple",
"🦄 Pot": "light-purple"
},
"bars": false,
"dashLength": 10,
"dashes": false,
"datasource": "${DS_GOOGLE_BIGQUERY}",
"fieldConfig": {
"defaults": {
"custom": {}
},
"overrides": []
},
"fill": 3,
"fillGradient": 0,
"gridPos": {
"h": 6,
"w": 12,
"x": 12,
"y": 14
},
"hiddenSeries": false,
"id": 17,
"legend": {
"avg": false,
"current": false,
"max": false,
"min": false,
"show": true,
"total": false,
"values": false
},
"lines": true,
"linewidth": 2,
"maxDataPoints": 100000000,
"nullPointMode": "null",
"options": {
"dataLinks": []
},
"percentage": false,
"pointradius": 2,
"points": false,
"renderer": "flot",
"seriesOverrides": [
{
"$$hashKey": "object:1875",
"alias": "avg",
"fill": 0,
"linewidth": 1,
"pointradius": 3,
"points": true
}
],
"spaceLength": 10,
"stack": false,
"steppedLine": false,
"targets": [
{
"format": "time_series",
"group": [],
"metricColumn": "none",
"orderByCol": "1",
"orderBySort": "1",
"rawQuery": true,
"rawSql": "WITH base AS (\nSELECT\n timestamp,\n \"balance\" as metric,\n sum(amount) OVER (ORDER BY timestamp ASC ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) * -1 as potbalance,\n balance\nFROM\n `russellsaw.personal_data.monzo_transactions_hourly`\nWHERE\n name LIKE (\"% Pot\")\n)\nSELECT \n TIMESTAMP_TRUNC(timestamp, DAY) as day,\n metric,\n avg(balance) + avg(potbalance) as balance\nFROM base\nGROUP BY day, metric",
"refId": "A",
"select": [
[
{
"params": [
"-- value --"
],
"type": "column"
}
]
],
"timeColumn": "-- time --",
"timeColumnType": "TIMESTAMP",
"where": [
{
"name": "$__timeFilter",
"params": [],
"type": "macro"
}
]
},
{
"format": "time_series",
"group": [],
"metricColumn": "none",
"orderByCol": "1",
"orderBySort": "1",
"rawQuery": true,
"rawSql": "WITH base AS (\nSELECT\n timestamp,\n \"avg\" as metric,\n sum(amount) OVER (ORDER BY timestamp ASC ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) * -1 as potbalance,\n balance\nFROM\n `russellsaw.personal_data.monzo_transactions_hourly`\nWHERE\n name LIKE (\"% Pot\")\n)\nSELECT \n TIMESTAMP_TRUNC(timestamp, MONTH) as day,\n metric,\n avg(balance) + avg(potbalance) as balance\nFROM base\nGROUP BY day, metric",
"refId": "B",
"select": [
[
{
"params": [
"-- value --"
],
"type": "column"
}
]
],
"timeColumn": "-- time --",
"timeColumnType": "TIMESTAMP",
"where": [
{
"name": "$__timeFilter",
"params": [],
"type": "macro"
}
]
}
],
"thresholds": [],
"timeFrom": "365d",
"timeRegions": [],
"timeShift": null,
"title": "Net Balance",
"tooltip": {
"shared": true,
"sort": 0,
"value_type": "individual"
},
"transparent": true,
"type": "graph",
"xaxis": {
"buckets": null,
"mode": "time",
"name": null,
"show": true,
"values": []
},
"yaxes": [
{
"$$hashKey": "object:319",
"format": "short",
"label": null,
"logBase": 1,
"max": null,
"min": null,
"show": true
},
{
"$$hashKey": "object:320",
"format": "short",
"label": null,
"logBase": 1,
"max": null,
"min": null,
"show": true
}
],
"yaxis": {
"align": false,
"alignLevel": null
}
},
{
"aliasColors": {
"count": "light-yellow",
"f0_": "light-yellow"
},
"bars": true,
"dashLength": 10,
"dashes": false,
"datasource": "${DS_GOOGLE_BIGQUERY}",
"fieldConfig": {
"defaults": {
"custom": {}
},
"overrides": []
},
"fill": 1,
"fillGradient": 0,
"gridPos": {
"h": 7,
"w": 12,
"x": 0,
"y": 20
},
"hiddenSeries": false,
"id": 15,
"legend": {
"avg": false,
"current": false,
"max": false,
"min": false,
"show": true,
"total": false,
"values": false
},
"lines": false,
"linewidth": 1,
"nullPointMode": "null",
"options": {
"dataLinks": []
},
"percentage": false,
"pointradius": 2,
"points": false,
"renderer": "flot",
"seriesOverrides": [],
"spaceLength": 10,
"stack": false,
"steppedLine": false,
"targets": [
{
"format": "time_series",
"group": [],
"metricColumn": "none",
"orderByCol": "1",
"orderBySort": "1",
"rawQuery": true,
"rawSql": "SELECT\n TIMESTAMP_TRUNC(timestamp, WEEK) as day,\n count(*) as count\nFROM\n `russellsaw.personal_data.monzo_transactions_hourly`\nWHERE\n $__timeFilter(timestamp)\nGROUP BY day\nORDER BY day ASC\n",
"refId": "A",
"select": [
[
{
"params": [
"-- value --"
],
"type": "column"
}
]
],
"timeColumn": "-- time --",
"timeColumnType": "TIMESTAMP",
"where": [
{
"name": "$__timeFilter",
"params": [],
"type": "macro"
}
]
}
],
"thresholds": [],
"timeFrom": null,
"timeRegions": [],
"timeShift": null,
"title": "Number of transactions per week",
"tooltip": {
"shared": true,
"sort": 0,
"value_type": "individual"
},
"transparent": true,
"type": "graph",
"xaxis": {
"buckets": null,
"mode": "time",
"name": null,
"show": true,
"values": []
},
"yaxes": [
{
"$$hashKey": "object:1575",
"format": "short",
"label": null,
"logBase": 1,
"max": null,
"min": null,
"show": true
},
{
"$$hashKey": "object:1576",
"format": "short",
"label": null,
"logBase": 1,
"max": null,
"min": null,
"show": true
}
],
"yaxis": {
"align": false,
"alignLevel": null
}
},
{
"aliasColors": {
"total": "semi-dark-orange",
"🚲 Pot balance": "light-yellow",
"🥑 Pot balance": "light-red",
"🦄 Pot balance": "light-purple"
},
"bars": false,
"dashLength": 10,
"dashes": false,
"datasource": "${DS_GOOGLE_BIGQUERY}",
"fieldConfig": {
"defaults": {
"custom": {}
},
"overrides": []
},
"fill": 3,
"fillGradient": 0,
"gridPos": {
"h": 6,
"w": 12,
"x": 12,
"y": 20
},
"hiddenSeries": false,
"id": 13,
"legend": {
"alignAsTable": true,
"avg": false,
"current": true,
"max": false,
"min": false,
"rightSide": true,
"show": true,
"total": false,
"values": true
},
"lines": true,
"linewidth": 3,
"maxDataPoints": "1000000",
"nullPointMode": "null as zero",
"options": {
"dataLinks": []
},
"percentage": false,
"pointradius": 2,
"points": false,
"renderer": "flot",
"seriesOverrides": [],
"spaceLength": 10,
"stack": false,
"steppedLine": false,
"targets": [
{
"format": "time_series",
"group": [],
"metricColumn": "none",
"orderByCol": "1",
"orderBySort": "1",
"rawQuery": true,
"rawSql": "SELECT\n timestamp,\n sum(amount) OVER (ORDER BY timestamp ASC ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) * -1 as total,\nFROM\n `russellsaw.personal_data.monzo_transactions_hourly`\nWHERE\n description = \"Round up\"\n AND $__timeFilter(timestamp)\n",
"refId": "A",
"select": [
[
{
"params": [
"-- value --"
],
"type": "column"
}
]
],
"timeColumn": "-- time --",
"timeColumnType": "TIMESTAMP",
"where": [
{
"name": "$__timeFilter",
"params": [],
"type": "macro"
}
]
}
],
"thresholds": [],
"timeFrom": null,
"timeRegions": [],
"timeShift": null,
"title": "Round Up",
"tooltip": {
"shared": true,
"sort": 0,
"value_type": "individual"
},
"transparent": true,
"type": "graph",
"xaxis": {
"buckets": null,
"mode": "time",
"name": null,
"show": true,
"values": []
},
"yaxes": [
{
"$$hashKey": "object:1350",
"format": "currencyGBP",
"label": null,
"logBase": 1,
"max": null,
"min": "0",
"show": true
},
{
"$$hashKey": "object:1351",
"format": "short",
"label": null,
"logBase": 1,
"max": null,
"min": null,
"show": true
}
],
"yaxis": {
"align": false,
"alignLevel": null
}
}
],
"refresh": "1h",
"schemaVersion": 25,
"style": "dark",
"tags": [],
"templating": {
"list": []
},
"time": {
"from": "now-90d",
"to": "now"
},
"timepicker": {
"refresh_intervals": [
"10s",
"30s",
"1m",
"5m",
"15m",
"30m",
"1h",
"2h",
"1d"
]
},
"timezone": "",
"title": "Monzo Data",
"uid": "WKTexIXWk",
"version": 12
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment