Skip to content

Instantly share code, notes, and snippets.

@alerque
Last active June 23, 2021 20:19
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save alerque/b9096aeb6829934c2256e80b91530ae0 to your computer and use it in GitHub Desktop.
Save alerque/b9096aeb6829934c2256e80b91530ae0 to your computer and use it in GitHub Desktop.
Makefile snippet to include to fetch prices for all transactions
# Usage:
# 1. from your main makefile set `MAIN_LEDGER = <path>` to something that has all your transactions
# 2. include this into your main makefile via `include prices.mk`
# 3. Have a fixer.io API key exported as FIXERAPIKEY (either in parent shell or makefile) before doing №4
# 4. periodically run `make update_prices`
# 5. include reltant price list into your main ledger via `include prices.ledger`
#
# Caveats:
# Fixer.io as an API call limit of 100 per day or something like that. That's per currency,
# so if you have 5 currencies in play you need to run this at every less than < 20 days to
# stay under the limit. Getting setup with years of history is hard, run once per day until
# you hit the limit and then wait until tomorrow. Eeach run as in №4 will make 10 day fetches
# so depending on your currency usage you may be able to run it a few times.
#
# Be sure to adjust the Git commit stuff if you don't want this making automatic commits!
#
# Requires moreutils (sponge), jq, curl, grep, sed, awk, perl, pcregrep, tee, etc.
#
# Update to include CODE‹›SYMBOL maps for your currencies
#
# This changes your makefile processing to use ZSH shell instead of sh. If that's a problem you'll need to adapt the code yourself.
SHELL := zsh
.SHELLFLAGS := +o nomatch -e -c
.ONESHELL:
.SECONDEXPANSION:
# Function to convert ISO currency codes to localized forms
define localcurrency =
perl -pne '
s/(\d+),(\d+)(\.\d+)?(?= \p{Lu}{3})/\1\2\3/g;
s/(?<=\s)(-?\d+)(\.(\d+))? TRY/₺\1,\3/g;
s/(?<=\s)(-?\d+)(\.(\d+))? USD/\$$\1.\3/g;
s/(?<=\s)(-?\d+)(\.(\d+))? EUR/€\1.\3/g;
s/(?<=\s)(-?\d+)(\.(\d+))? KZT/\1,\3 ₸/g;
s/(?<=\s)(-?\d+)(\.(\d+))? GBP/₤\1.\3/g;
s/(?<=\s)(-?\d+)(\.(\d+))? BGN/\1,\3 лв/g;
s/(?<=\s)(-?\d+)(\.(\d+))? TND/\1.\3 دت/g;
s/(?<=\s)(-?\d+)(\.(\d+))? CHF/Fr \1.\3/g;
s/(?<=\s)(-?\d+)(\.(\d+))? XOF/CFA \1.\3/g;
'
endef
# Function to localized forms to convert ISO currency codes
define isocurrency =
perl -pne 's/₺/TRY/g;s/\$$/USD/g;s/€/EUR/g;s/₸/KZT/g;s/₤/GBP/g;s/лв/BGN/g;s/دت/TND/g;s/\bFr /CHF/g;s/CFA/XOF/g;s/(?<=\d) (?=\d)//g if /^ /'
endef
prices.ledger: dates.json commodities.json
apicalls=0
jq -r '.commodities | join(",")' commodities.json | read symbols
jq -r '.dates[]' dates.json |
sed 's#/#-#g' |
awk 'FNR>1 && $$1<"$(shell date +%Y-%m-%d)"' | # Don't fetch today or later because market rates aren't settled
tac | # Prioritize API usage for newest dates, historical can be added later
while read date; do
if ! grep -q "^P $${date}" $@; then
if [[ $${apicalls} -lt 10 ]]; then
# curl -s "https://api.exchangeratesapi.io/$${date}?base=EUR&symbols=$${symbols}"
# curl -s "https://api.ratesapi.io/api/$${date}?base=EUR&symbols=$${symbols}"
curl -s "http://data.fixer.io/api/$${date}?access_key=$(FIXERAPIKEY)&symbols=$${symbols}"
fi
apicalls=$$(($${apicalls}+1))
fi
done |
jq -rs '.[] |
. as $$res |
.rates |
to_entries[] |
[ "P",
$$res.date,
"USD",
(.value / $$res.rates.USD * 10000 | round / 10000 | tostring),
.key
] |
join(" ")' |
( tee >($(localcurrency) | sed -e 's/ USD / $$ /') ) |
sed -e '/\$$ \$$1/d;s/USD 1 USD/USD $$1/' |
hledger -f $@ -f- prices |
sponge $@
.PHONY: update_prices
update_prices: prices.ledger
git diff-index --quiet --cached HEAD || exit 1 # die if anything already staged
test -s $< || exit 1 # die if for some reason the prices file ends up empty
git add -- $<
git diff-index --quiet --cached HEAD || git commit -m "[auto] Update market prices list"
dates.json: $(MAIN_LEDGER)
hledger -f $< print -I |
awk -F'[ =]' '/^[[:digit:]]/ { print $$1 }' |
jq -nR '{ "dates": ([inputs] | unique) }' > $@
commodities.json: $(MAIN_LEDGER)
hledger -f <(grep -v '^include' $<) commodities -I |
pcregrep -x '\p{Lu}{3}' | grep -v '^CFA$$' |
jq -nR '{ "commodities": ([inputs] | unique) }' > $@
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment