From 7fb83e187a290408a4e3ac425e19d8b6f2e514ac Mon Sep 17 00:00:00 2001 From: Dennis Reimann Date: Tue, 28 Apr 2020 21:43:55 +0200 Subject: [PATCH] =?UTF-8?q?=F0=9F=8E=89=20Get=20the=20party=20started?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 2 + README.md | 64 +++++++++++++++++++ index.js | 49 ++++++++++++++ package-lock.json | 159 ++++++++++++++++++++++++++++++++++++++++++++++ package.json | 16 +++++ 5 files changed, 290 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 index.js create mode 100644 package-lock.json create mode 100644 package.json diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6ed48a9 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.env +node_modules diff --git a/README.md b/README.md new file mode 100644 index 0000000..50415ab --- /dev/null +++ b/README.md @@ -0,0 +1,64 @@ +# Stacking Sats on Kraken + +First off: Here's to you, [Bittr](https://getbittr.com/) โ€“ you will be missed! ๐Ÿ˜ข + +This script is not a full replacement for the incredible service Bittr offered, but it's a start: +Automate your Stacking Sats process by regularly placing buy orders using the [Kraken API](https://www.kraken.com/features/api). + +## โœ‹ Caveat + +You need to install the dependency [kraken-api](https://github.com/nothingisdead/npm-kraken-api), which is a third-party package. +It has a minimal set of dependencies and I've done my best to audit its code. +Also the version is fixed, so that unwanted changes do not slip in. + +However: Use this at your own risk and decide for yourself whether or not you want to run this script and its dependencies! + +## ๐Ÿ“ฆ Setup + +Prerequisite: At least the current LTS version of [Node.js](https://nodejs.org/). + +Install the dependencies: + +```sh +npm install +``` + +Setup the environment variables for the script: + +```sh +export KRAKEN_API_KEY="apiKeyFromTheKrakenSettings" +export KRAKEN_API_SECRET="privateKeyFromTheKrakenSettings" +export KRAKEN_API_FIAT="USD" # the governmental shitcoin you are selling +export KRAKEN_BUY_AMOUNT=21 # fiat amount you trade for the future of money +``` + +Use a dry run to test the script and see the output without placing an order: + +```sh +npm test +``` + +You should see something like this sample output: + +```text +๐Ÿ’ฐ Balance: 210000.00 USD / 21.0 XBT + +๐Ÿ“ˆ Ask: 21000.2 USD +๐Ÿ“‰ Bid: 21000.1 USD + +๐Ÿงพ Order: 0.21212121 XBT at 21000.1 USD + +๐Ÿ’ธ Placed order: buy 0.21212121 XBTUSD @ limit 21000.1 / TXID: 2121212121 +``` + +## ๐Ÿค‘ Stack sats + +When you are good to go, execute this command in a regular interval: + +```sh +npm run stack-sats +``` + +Now go wild and triggeer it via a weekly or even daily cron job. + +[Stay humble!](https://twitter.com/matt_odell/status/1117222441867194374) ๐Ÿ™ diff --git a/index.js b/index.js new file mode 100644 index 0000000..7e8e825 --- /dev/null +++ b/index.js @@ -0,0 +1,49 @@ +const assert = require('assert') +const Kraken = require('kraken-api') + +const { + KRAKEN_API_KEY: key, + KRAKEN_API_SECRET: secret, + KRAKEN_API_FIAT: fiat, + KRAKEN_BUY_AMOUNT: amount +} = process.env + +assert(key && secret, 'Provide the KRAKEN_API_KEY and KRAKEN_API_SECRET environment variables.') +assert(fiat && amount, 'Provide the KRAKEN_API_FIAT and KRAKEN_BUY_AMOUNT environment variables.') + +// https://www.kraken.com/features/api +const kraken = new Kraken(key, secret) +const crypto = 'XBT' +const pair = `X${crypto}Z${fiat}` +const validate = process.argv[2] === '--validate' + +;(async () => { + // Fetch and display information + const { result: { [`Z${fiat}`]: fiatBalance, [`X${crypto}`]: cryptoBalance } } = await kraken.api('Balance') + const { result: { [pair]: { a: [a], b: [b] } } } = await kraken.api('Ticker', { pair }) + + const ask = parseFloat(a) + const bid = parseFloat(b) + const price = bid + + // Calculate volume and adjust precision + const volume = (amount / price).toFixed(8) + + console.log('\n') + console.log('๐Ÿ’ฐ Balance:', fiatBalance, fiat, '/', cryptoBalance, crypto, '\n') + console.log('๐Ÿ“ˆ Ask:', ask, fiat) + console.log('๐Ÿ“‰ Bid:', bid, fiat, '\n') + console.log('๐Ÿงพ Order:', volume, crypto, 'at', price, fiat, '\n') + + // Place order + try { + const details = { pair, type: 'buy', ordertype: 'limit', price, volume, validate } + const { result: { descr: { order } }, txid } = await kraken.api('AddOrder', details) + + console.log('๐Ÿ’ธ Placed order:', order, '/ TXID:', txid, '\n') + } catch (err) { + console.error(`๐Ÿšจ Failure:`, err.message, '\n') + } finally { + if (validate) console.warn('๐Ÿšจ THIS WAS JUST A VALIDATION RUN, NO ORDER HAS BEEN PLACED!', '\n') + } +})() diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..4c6c813 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,159 @@ +{ + "name": "kraken-dca", + "version": "0.1.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "decompress-response": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", + "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=", + "requires": { + "mimic-response": "^1.0.0" + } + }, + "duplexer3": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", + "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=" + }, + "get-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", + "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=" + }, + "got": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/got/-/got-7.1.0.tgz", + "integrity": "sha512-Y5WMo7xKKq1muPsxD+KmrR8DH5auG7fBdDVueZwETwV6VytKyU9OX/ddpq2/1hp1vIPvVb4T81dKQz3BivkNLw==", + "requires": { + "decompress-response": "^3.2.0", + "duplexer3": "^0.1.4", + "get-stream": "^3.0.0", + "is-plain-obj": "^1.1.0", + "is-retry-allowed": "^1.0.0", + "is-stream": "^1.0.0", + "isurl": "^1.0.0-alpha5", + "lowercase-keys": "^1.0.0", + "p-cancelable": "^0.3.0", + "p-timeout": "^1.1.1", + "safe-buffer": "^5.0.1", + "timed-out": "^4.0.0", + "url-parse-lax": "^1.0.0", + "url-to-options": "^1.0.1" + } + }, + "has-symbol-support-x": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/has-symbol-support-x/-/has-symbol-support-x-1.4.2.tgz", + "integrity": "sha512-3ToOva++HaW+eCpgqZrCfN51IPB+7bJNVT6CUATzueB5Heb8o6Nam0V3HG5dlDvZU1Gn5QLcbahiKw/XVk5JJw==" + }, + "has-to-string-tag-x": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/has-to-string-tag-x/-/has-to-string-tag-x-1.4.1.tgz", + "integrity": "sha512-vdbKfmw+3LoOYVr+mtxHaX5a96+0f3DljYd8JOqvOLsf5mw2Otda2qCDT9qRqLAhrjyQ0h7ual5nOiASpsGNFw==", + "requires": { + "has-symbol-support-x": "^1.4.1" + } + }, + "is-object": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-object/-/is-object-1.0.1.tgz", + "integrity": "sha1-iVJojF7C/9awPsyF52ngKQMINHA=" + }, + "is-plain-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", + "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=" + }, + "is-retry-allowed": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-1.2.0.tgz", + "integrity": "sha512-RUbUeKwvm3XG2VYamhJL1xFktgjvPzL0Hq8C+6yrWIswDy3BIXGqCxhxkc30N9jqK311gVU137K8Ei55/zVJRg==" + }, + "is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" + }, + "isurl": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isurl/-/isurl-1.0.0.tgz", + "integrity": "sha512-1P/yWsxPlDtn7QeRD+ULKQPaIaN6yF368GZ2vDfv0AL0NwpStafjWCDDdn0k8wgFMWpVAqG7oJhxHnlud42i9w==", + "requires": { + "has-to-string-tag-x": "^1.2.0", + "is-object": "^1.0.1" + } + }, + "kraken-api": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/kraken-api/-/kraken-api-1.0.0.tgz", + "integrity": "sha512-JAfLh9Laks+2KZTM/WW+bpy+iZfImUok0aeLBOb0ehwTEGvcBWWAXlmkthhJXfUTSoOIHuoqqToC94+iBeBBAg==", + "requires": { + "got": "^7.1.0", + "qs": ">=6.4.0" + } + }, + "lowercase-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", + "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==" + }, + "mimic-response": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", + "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==" + }, + "p-cancelable": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-0.3.0.tgz", + "integrity": "sha512-RVbZPLso8+jFeq1MfNvgXtCRED2raz/dKpacfTNxsx6pLEpEomM7gah6VeHSYV3+vo0OAi4MkArtQcWWXuQoyw==" + }, + "p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=" + }, + "p-timeout": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-1.2.1.tgz", + "integrity": "sha1-XrOzU7f86Z8QGhA4iAuwVOu+o4Y=", + "requires": { + "p-finally": "^1.0.0" + } + }, + "prepend-http": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz", + "integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=" + }, + "qs": { + "version": "6.9.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.3.tgz", + "integrity": "sha512-EbZYNarm6138UKKq46tdx08Yo/q9ZhFoAXAI1meAFd2GtbRDhbZY2WQSICskT0c5q99aFzLG1D4nvTk9tqfXIw==" + }, + "safe-buffer": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz", + "integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==" + }, + "timed-out": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/timed-out/-/timed-out-4.0.1.tgz", + "integrity": "sha1-8y6srFoXW+ol1/q1Zas+2HQe9W8=" + }, + "url-parse-lax": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-1.0.0.tgz", + "integrity": "sha1-evjzA2Rem9eaJy56FKxovAYJ2nM=", + "requires": { + "prepend-http": "^1.0.1" + } + }, + "url-to-options": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/url-to-options/-/url-to-options-1.0.1.tgz", + "integrity": "sha1-FQWgOiiaSMvXpDTvuu7FBV9WM6k=" + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..b385002 --- /dev/null +++ b/package.json @@ -0,0 +1,16 @@ +{ + "private": true, + "name": "stacking-sats-kraken", + "version": "0.1.0", + "description": "Use the Kraken API to stack sats", + "author": "Dennis Reimann ", + "license": "MIT", + "main": "index.js", + "scripts": { + "stack-sats": "node index.js", + "test": "node index.js --validate" + }, + "dependencies": { + "kraken-api": "1.0.0" + } +}