10 Commits

Author SHA1 Message Date
Dennis Reimann
e133d56072 0.3.0 2020-11-10 21:43:21 +01:00
Dennis Reimann
fa7fe94d6d 🛠 Rename scripts and unify shell script 2020-11-10 21:42:52 +01:00
Dennis Reimann
7fd9fe1043 🐛 Fix pair names
Allows for usage with CHF.
2020-11-10 21:00:44 +01:00
Dennis Reimann
0e571e8965 🛠 Refactor to use commands 2020-11-10 11:17:07 +01:00
Dennis Reimann
d1b2052494 🧤 Cleanups 2020-11-09 21:50:51 +01:00
Tobias Koller
a9694ac1dd Withdrawal script (#4)
* Withdrawal script. Added a script that lets you withdraw through Kraken API if the charged fees are under a certain level

* refactored code and added validation functionality

* Enhanced README to reflect the option to also withdraw in an automated manner

* fixed typo
2020-11-09 21:15:52 +01:00
Dennis Reimann
c387bb3ea3 ️ Document RaspiBlitz Integration 2020-08-24 14:18:45 +02:00
Dennis Reimann
4a2fb94dc4 🔑 Document API Key 2020-08-24 14:18:21 +02:00
Dennis Reimann
2a6bd0961b 🔖 Release v0.2.0 2020-07-01 13:06:23 +02:00
Dennis Reimann
04e0ed39ba ✍️ Improve output 2020-07-01 13:03:40 +02:00
8 changed files with 201 additions and 55 deletions

1
.gitignore vendored
View File

@@ -1,2 +1,3 @@
.env
node_modules
stacksats.sh

131
README.md
View File

@@ -13,6 +13,16 @@ 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!
## 🔑 API Key
Obtain your Kraken API Key via [the API settings page](https://www.kraken.com/u/settings/api).
Generate a new API key dedicated for stacking using the "Query Funds" and "Modify Orders" permissions:
![Kraken API Key Permissions](./api-permissions.png)
Only check the "Withdraw Funds" option if you plan to automatically withdraw Bitcoin from Kraken.
See details below.
## 📦 Setup
Prerequisite: At least the current LTS version of [Node.js](https://nodejs.org/).
@@ -26,16 +36,26 @@ npm install
Setup the environment variables for the script:
```sh
# used to authenticate with Kraken
export KRAKEN_API_KEY="apiKeyFromTheKrakenSettings"
export KRAKEN_API_SECRET="privateKeyFromTheKrakenSettings"
# used for buying
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
# used for withdrawal
export KRAKEN_MAX_REL_FEE=0.5 # maximum fee in % that you are willing to pay
export KRAKEN_WITHDRAW_KEY="descriptionOfWithdrawalAddress"
# remove this line after verifying everything works
export KRAKEN_DRY_RUN_PLACE_NO_ORDER=1
```
Use a dry run to test the script and see the output without placing an order:
```sh
npm test
npm run test:stack
```
You should see something like this sample output:
@@ -46,9 +66,8 @@ You should see something like this sample output:
📈 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
💸 Order: buy 0.21212121 XBTUSD @ limit 21000.1
📎 Transaction ID: 2121212121
```
## 🤑 Stack sats
@@ -56,13 +75,13 @@ You should see something like this sample output:
When you are good to go, execute this command in a regular interval:
```sh
npm run stack-sats
npm run stack
```
The best and easiest way is to wrap it all up in a shell script.
This script can be triggered via cron job, e.g. weekly, daily or hourly.
Here's a sample `stack-sats.sh` script:
Here's a sample `stacksats.sh` script:
```sh
#!/bin/bash
@@ -75,19 +94,111 @@ export KRAKEN_API_KEY="apiKeyFromTheKrakenSettings"
export KRAKEN_API_SECRET="privateKeyFromTheKrakenSettings"
export KRAKEN_API_FIAT="USD"
export KRAKEN_BUY_AMOUNT=21
export KRAKEN_MAX_REL_FEE=0.5
export KRAKEN_WITHDRAW_KEY="descriptionOfWithdrawalAddress"
export KRAKEN_DRY_RUN_PLACE_NO_ORDER=1
# run script
BASE_DIR=$(cd `dirname $0` && pwd)
cd $BASE_DIR/stacking-sats-kraken
result=$(npm run stack-sats 2>&1)
echo $result
cmd=${1:-"stack"}
# Optional: Send yourself an email
if [[ "${KRAKEN_DRY_RUN_PLACE_NO_ORDER}" ]]; then
result=$(npm run test:$cmd --silent 2>&1)
else
result=$(npm run $cmd --silent 2>&1)
fi
echo "$result"
# optional: send yourself an email
recipient="satstacker@example.org"
echo "Subject: Sats got stacked
From: satstacker@example.org
To: $recipient $result" | /usr/sbin/sendmail $recipient
```
Make it executable with `chmod +x stack-sats.sh` and go wild.
Make it executable with `chmod +x stacksats.sh` and go wild.
[Stay humble!](https://twitter.com/matt_odell/status/1117222441867194374) 🙏
## 🔑 Withdrawal
Holding significant amounts on an exchange is never a good idea.
You should regularly take ownership of your coins by withdrawing to your own wallet.
This can either be done manually or it can be automated.
The script provided here will only withdraw to a previously defined Bitcoin address if the relative fees do not exceed a certain limit.
*It is optional to run the withdrawal script.*
### Example 1
- Max. relative fee: 0.5%
- Fixed Kraken fee: ₿ 0.00050
- Balance: ₿ 0.06000
➡️ No withdrawal since fee actual (0.83%) is too high
### Example 2
- Max. relative fee: 0.5%
- Fixed Kraken fee: ₿ 0.00050
- Balance: ₿ 0.12000
➡️ Withdrawal executed since actual fee (0.42%) is low enough
In case you plan to automatically withdraw from Kraken, a withdrawal method must first be defined.
If you already set up a methode you can reuse it.
Otherwise generate a new one by going to **Funding > Bitcoin (XBT) withdraw > Add address**.
The description field will later be used as an environment variable in the script.
To test the withdrawal of funds to your defined address run:
```sh
npm run test:withdraw
```
You should see something like this:
```text
💡 Relative fee of withdrawal amount: 5.57%
❌ Fee is too high  max rel. fee: 0.50%
```
It is recommended to run the withdrawal script every time you stacked sats:
```sh
npm run withdraw
```
Since it can take a couple seconds or minutes for your order to fill, you should run the following script a couple hours later after the stacking script.
Just set up a second cron job which executes the withdrawal script.
If you are using the aforementioned `stacksats.sh` script you can withdraw via this command:
`stacksats.sh withdraw`
## ⚡️ RaspiBlitz Integration
This script ships with the [RaspiBlitz](https://github.com/rootzoll/raspiblitz) (v1.6 and above).
You can enable it via the Console of your RaspiBlitz.
Leave the main menu via the last option "Console" and use the following commands:
```sh
# enable the script
./config.scripts/bonus.stacking-sats-kraken.sh on
# switch to the stackingsats user
sudo su - stackingsats
# edit your configuration (see "Setup" above)
nano /mnt/hdd/app-data/stacking-sats-kraken/.env
# follow the instructions from the first step to set up a cronjob
crontab -e
```
Here is an example for a daily cronjob at 6:15am ...
```sh
SHELL=/bin/bash
PATH=/bin:/usr/sbin:/usr/bin:/usr/local/bin
15 6 * * * /home/stackingsats/stacksats.sh > /dev/null 2>&1
```

BIN
api-permissions.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

34
commands/stack.js Normal file
View File

@@ -0,0 +1,34 @@
module.exports = async (kraken, validate, getEnv) => {
const [fiat, amount] = getEnv('KRAKEN_API_FIAT', 'KRAKEN_BUY_AMOUNT')
// https://www.kraken.com/features/api
const crypto = 'XBT'
const pair = `${crypto}${fiat}`
// Fetch and display information
const { result: balance } = await kraken.api('Balance')
const { result: ticker } = await kraken.api('Ticker', { pair })
const fiatBalance = balance[`Z${fiat}`] || balance[fiat] || 0.0
const cryptoBalance = balance[`X${crypto}`] || balance[crypto] || 0.0
const [{ a: [a], b: [b] }] = Object.values(ticker)
const ask = parseFloat(a)
const bid = parseFloat(b)
const price = bid
// Calculate volume and adjust precision
const volume = (amount / price).toFixed(8)
console.log('💰 Balance:', fiatBalance, fiat, '/', cryptoBalance, crypto, '\n')
console.log('📈 Ask:', ask, fiat)
console.log('📉 Bid:', bid, fiat, '\n')
// Place order
const details = { pair, type: 'buy', ordertype: 'limit', price, volume }
if (validate) details.validate = true
const { result: { descr: { order }, txid } } = await kraken.api('AddOrder', details)
console.log('💸 Order:', order)
if (txid) console.log('📎 Transaction ID:', txid.join(', '))
}

25
commands/withdraw.js Normal file
View File

@@ -0,0 +1,25 @@
module.exports = async (kraken, validate, getEnv) => {
const [maxFee, key] = getEnv('KRAKEN_MAX_REL_FEE', 'KRAKEN_WITHDRAW_KEY')
// https://api.kraken.com/0/private/WithdrawInfo
const asset = 'XBT'
const withdrawdetails = { asset, key, amount: 0 }
// Get withdrawal information
const { result: { limit, fee } } = await kraken.api('WithdrawInfo', withdrawdetails)
const relFee = 1/parseFloat(limit)*parseFloat(fee)
console.log(`💡 Relative fee of withdrawal amount: ${(relFee*100).toFixed(2)}%`)
// Place withdrawal when fee is low enough (relatively)
if (relFee < maxFee/100) {
console.log(`💰 Withdraw ${limit} ${asset} now.`)
const withdraw = { asset, key, amount: limit }
if (!validate) {
const { result: { refid } } = await kraken.api('Withdraw', withdraw)
if (refid) console.log(`📎 Withdrawal reference ID: ${refid}`)
}
} else {
console.log(`❌ Fee is too high - max rel. fee: ${parseFloat(maxFee).toFixed(2)}%`)
}
}

View File

@@ -1,51 +1,24 @@
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'
const getEnv = (...vars) => vars.map(name => {
const value = process.env[name]
assert(value, `Provide the ${name} environment variable.`)
return value
})
const command = process.argv[2].replace('--cmd=', '')
const validate = process.argv.includes('--validate') || process.env['KRAKEN_DRY_RUN_PLACE_NO_ORDER']
;(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 }
if (validate) details.validate = true
const [apiKey, secret] = getEnv('KRAKEN_API_KEY', 'KRAKEN_API_SECRET')
const kraken = new Kraken(apiKey, secret)
const { result: { descr: { order }, txid } } = await kraken.api('AddOrder', details)
const cmd = require(`./commands/${command}`)
await cmd(kraken, validate, getEnv)
console.log('💸 Placed order:', order, '/ TXID:', txid, '\n')
if (validate) console.log('\n🚨 THIS WAS JUST A VALIDATION RUN!')
} catch (err) {
console.log(`🚨 Failure:`, err.message, '\n')
} finally {
if (validate) console.warn('🚨 THIS WAS JUST A VALIDATION RUN, NO ORDER HAS BEEN PLACED!', '\n')
console.log(`\n🚨 Failure:`, err.message)
}
})()

2
package-lock.json generated
View File

@@ -1,6 +1,6 @@
{
"name": "stacking-sats-kraken",
"version": "0.1.0",
"version": "0.3.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {

View File

@@ -1,14 +1,16 @@
{
"private": true,
"name": "stacking-sats-kraken",
"version": "0.1.0",
"version": "0.3.0",
"description": "Use the Kraken API to stack sats",
"author": "Dennis Reimann <mail@dennisreimann.de>",
"license": "MIT",
"main": "index.js",
"main": "stack.js",
"scripts": {
"stack-sats": "node index.js",
"test": "node index.js --validate"
"stack": "node index.js --cmd=stack",
"withdraw": "node index.js --cmd=withdraw",
"test:stack": "node index.js --cmd=stack --validate",
"test:withdraw": "node index.js --cmd=withdraw --validate"
},
"dependencies": {
"kraken-api": "1.0.0"