Tracking Bitcoin Price (#5)
This commit is contained in:
@@ -11,6 +11,7 @@ This is a collection of useful scripts to help you manage your Actual Budget.
|
|||||||
- [Tracking Home Prices (Zillow's Zestimate)](#tracking-home-prices-zillows-zestimate)
|
- [Tracking Home Prices (Zillow's Zestimate)](#tracking-home-prices-zillows-zestimate)
|
||||||
- [Tracking Car Prices (Kelley Blue Book)](#tracking-car-prices-kelley-blue-book)
|
- [Tracking Car Prices (Kelley Blue Book)](#tracking-car-prices-kelley-blue-book)
|
||||||
- [Tracking Investment Accounts](#tracking-investment-accounts)
|
- [Tracking Investment Accounts](#tracking-investment-accounts)
|
||||||
|
- [Tracking Bitcoin Price](#tracking-bitcoin-price)
|
||||||
|
|
||||||
## Requirements
|
## Requirements
|
||||||
|
|
||||||
@@ -50,6 +51,11 @@ INVESTMENT_CATEGORY_NAME="Investment"
|
|||||||
|
|
||||||
# optional, for logging into SimpleFIN
|
# optional, for logging into SimpleFIN
|
||||||
SIMPLEFIN_CREDENTIALS="<credentials - not the setup token!>"
|
SIMPLEFIN_CREDENTIALS="<credentials - not the setup token!>"
|
||||||
|
|
||||||
|
# optional, for retrieving Bitcoin Price (these default to Kraken USD)
|
||||||
|
BITCOIN_PRICE_URL="https://api.kraken.com/0/public/Ticker?pair=xbtusd"
|
||||||
|
BITCOIN_PRICE_JSON_PATH="result.XXBTZUSD.c[0]"
|
||||||
|
BITCOIN_PAYEE_NAME="Bitcoin Price Change"
|
||||||
```
|
```
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
@@ -252,3 +258,23 @@ node track-investments.js
|
|||||||
|
|
||||||
It is recommended to run this script once per month.
|
It is recommended to run this script once per month.
|
||||||
|
|
||||||
|
### Tracking Bitcoin Price
|
||||||
|
|
||||||
|
This script tracks the value of Bitcoin. It adds new transactions to keep the
|
||||||
|
account balance equal to the latest value. There is one tag you can set in the
|
||||||
|
account notes, BTC:X, where X is the number of Bitcoin you own, eg `BTC:0.01`
|
||||||
|
You can optionally change the API endpoint used to retrieve the Bitcoin price,
|
||||||
|
an example for retrieving the price in GBP is:
|
||||||
|
|
||||||
|
```
|
||||||
|
BITCOIN_PRICE_URL="https://api.kraken.com/0/public/Ticker?pair=xbtgbp"
|
||||||
|
BITCOIN_PRICE_JSON_PATH=".result.XXBTZGBP.c[0]"
|
||||||
|
```
|
||||||
|
|
||||||
|
To run:
|
||||||
|
|
||||||
|
```console
|
||||||
|
node sync-bitcoin.js
|
||||||
|
```
|
||||||
|
|
||||||
|
It is recommended to run this script once per day or week.
|
||||||
|
|||||||
@@ -0,0 +1,71 @@
|
|||||||
|
const { closeBudget, openBudget, getTransactions, getAccountNote, getAccountBalance, ensurePayee } = require('./utils');
|
||||||
|
const api = require('@actual-app/api');
|
||||||
|
|
||||||
|
function getValueAtPath(obj, path) {
|
||||||
|
const keys = path.split('.').filter(Boolean);
|
||||||
|
|
||||||
|
return keys.reduce((acc, key) => {
|
||||||
|
const match = key.match(/^([^\[\]]+)(\[(\d+)\])?$/);
|
||||||
|
|
||||||
|
if (match) {
|
||||||
|
const property = match[1];
|
||||||
|
const index = match[3];
|
||||||
|
|
||||||
|
acc = acc[property];
|
||||||
|
|
||||||
|
if (index !== undefined) {
|
||||||
|
acc = acc[parseInt(index, 10)];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
acc = acc[key];
|
||||||
|
}
|
||||||
|
|
||||||
|
return acc;
|
||||||
|
}, obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getBitcoinPrice() {
|
||||||
|
const url = process.env.BITCOIN_PRICE_URL || "https://api.kraken.com/0/public/Ticker?pair=xbtusd"
|
||||||
|
const path = process.env.BITCOIN_PRICE_JSON_PATH || "result.XXBTZUSD.c[0]"
|
||||||
|
try {
|
||||||
|
response = await fetch(url);
|
||||||
|
const json = await response.json();
|
||||||
|
return getValueAtPath(json, path);
|
||||||
|
} catch (error) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
(async () => {
|
||||||
|
const bitcoinPrice = await getBitcoinPrice();
|
||||||
|
if (!bitcoinPrice) {
|
||||||
|
throw new Error("Unable to retrieve Bitcoin price. Check your BITCOIN_PRICE_URL and BITCOIN_PRICE_JSON_PATH environment variables");
|
||||||
|
}
|
||||||
|
await openBudget();
|
||||||
|
const payeeId = await ensurePayee(process.env.BITCOIN_PAYEE_NAME || 'Bitcoin Price Change');
|
||||||
|
const accounts = await api.getAccounts();
|
||||||
|
for (const account of accounts) {
|
||||||
|
if (account.closed) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const note = await getAccountNote(account);
|
||||||
|
if (!note || note.indexOf("BTC:") === -1) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const btc_amount = note.split('BTC:')[1].split(' ')[0];
|
||||||
|
const currentBalance = await getAccountBalance(account);
|
||||||
|
const targetBalance = Math.round(bitcoinPrice * btc_amount * 100);
|
||||||
|
const diff = currentBalance - targetBalance;
|
||||||
|
if (diff != 0) {
|
||||||
|
await api.importTransactions(account.id, [{
|
||||||
|
date: new Date(),
|
||||||
|
payee: payeeId,
|
||||||
|
amount: diff,
|
||||||
|
cleared: true,
|
||||||
|
reconciled: true,
|
||||||
|
notes: "Updated Bitcoin Price",
|
||||||
|
}])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await closeBudget();
|
||||||
|
})();
|
||||||
Reference in New Issue
Block a user