diff options
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | LICENSE | 21 | ||||
-rw-r--r-- | README.org | 40 | ||||
-rw-r--r-- | index.js | 68 | ||||
-rw-r--r-- | package-lock.json | 31 | ||||
-rw-r--r-- | package.json | 25 |
6 files changed, 186 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..07e6e47 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/node_modules diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..a53abb1 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2017 Alan Pearce + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.org b/README.org new file mode 100644 index 0000000..f0ff157 --- /dev/null +++ b/README.org @@ -0,0 +1,40 @@ +* Bitcoincharts Beancount converter + +This is a quick tool I hacked together to convert historic bitcoin +prices from [[http://api.bitcoincharts.com/v1/csv/][bitcoincharts CSVs]] into beancount price directives. + +The files are gzipped CSVs of (unixtime, price, tradeamount) + +For each month with trades on the 1st, the tool calculates a weighted +average of trades on that day and outputs a beancount price directive. + +** Running + +The tool does not download files on its own. Download the files +before running. + +#+BEGIN_SRC shell +$ node index.js some.csv.gz +# or +$ node index.js some.csv.gz EUR +# or +$ node index.js some.csv.gz EUR BTC +#+END_SRC + +*** Parameters + +- filename (gzipped CSV) +- currency [optional] (currency code used in beancount {USD,EUR, etc}) +- commodity [optional] (currency code of commodity) {BTC,BCH,LTC, etc} + +*** Status + +It works. I don't plan on working on it much further. + +It's not very efficent. I suspect the main problem is the group +function probably doesn't assume that the input is sorted and +therefore buffers its entire input. Patches welcome. + +** License + +MIT diff --git a/index.js b/index.js new file mode 100644 index 0000000..6152685 --- /dev/null +++ b/index.js @@ -0,0 +1,68 @@ +const r = require('ramda') +const h = require('highland') +const fs = require('fs') +const path = require('path') +const zlib = require('zlib') +const BigNumber = require('bignumber.js') + +BigNumber.config({ + DECIMAL_PLACES: 8 +}) + +if (process.argv.length < 3) { + console.error(`${path.basename(process.argv[1])} + +bitcoincharts historic price conversion + +Input: gzipped csv of (unixtime, price, tradeamount) + +Outputs beancount price directives for the weighted average price +of all trades executed on the first of each month in the provided file + +Download files from http://api.bitcoincharts.com/v1/csv/ + +usage: ${path.basename(process.argv[1])} FILENAME [CURRENCY] [COMMODITY]`) + process.exit(1) +} + +const [ + ,, + filename, + currency = 'EUR', + commodity = 'BTC' +] = process.argv + +h( + fs.createReadStream(filename) + .pipe(zlib.createGunzip()) +) + .split() + .filter(line => line.length > 1) + .map(line => line.split(',', 3)) + .map(([time, price, amount]) => [ + new Date(parseInt(time, 10) * 1000), + new BigNumber(price), + new BigNumber(amount) + ]) + .filter(([date]) => date.getDate() === 1) + .group(([date]) => date.toDateString()) + .map(group => Object.values(group)) + .sequence() + .map(group => group + .map( + ([date, price, amount]) => [date, price.times(amount), amount] + ) + .reduce( + ([date, sumPrice, sumAmount], [,weightPrice, amount]) => [ + date, + sumPrice.plus(weightPrice), + sumAmount.plus(amount) + ] + )) + .map(([date, sumprice, sumamount]) => [date, sumprice.dividedBy(sumamount)]) + .map(([date, avg]) => + `${date.toISOString().substr(0, 10)} price ${commodity} ${avg.toFixed(2)} ${currency}` + ) + .intersperse("\n") + .pipe(process.stdout) + diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..7ee272f --- /dev/null +++ b/package-lock.json @@ -0,0 +1,31 @@ +{ + "name": "bitcoincharts-beancount", + "version": "1.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "bignumber.js": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-4.0.4.tgz", + "integrity": "sha512-LDXpJKVzEx2/OqNbG9mXBNvHuiRL4PzHCGfnANHMJ+fv68Ads3exDVJeGDJws+AoNEuca93bU3q+S0woeUaCdg==" + }, + "highland": { + "version": "2.11.1", + "resolved": "https://registry.npmjs.org/highland/-/highland-2.11.1.tgz", + "integrity": "sha1-ObTZKZtuB9o9FeeveypvEnUirK8=", + "requires": { + "util-deprecate": "1.0.2" + } + }, + "ramda": { + "version": "0.24.1", + "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.24.1.tgz", + "integrity": "sha1-w7d1UZfzW43DUCIoJixMkd22uFc=" + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..20e849f --- /dev/null +++ b/package.json @@ -0,0 +1,25 @@ +{ + "name": "bitcoincharts-beancount", + "version": "1.0.0", + "description": "bitcoincharts historic price conversion", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "repository": { + "type": "git", + "url": "https://git.alanpearce.eu/bitcoincharts-beancount" + }, + "keywords": [ + "bitcoin", + "beancount", + "accounting" + ], + "author": "Alan Pearce <alan@alanpearce.eu>", + "license": "MIT", + "dependencies": { + "bignumber.js": "^4.0.4", + "highland": "^2.11.1", + "ramda": "^0.24.1" + } +} |