node-kraken-api
Interfaces with the Kraken cryptocurrency exchange API. Observes rate limits. Parses response JSON, converts stringed numbers, and normalizes timestamps. Facilitates persistent data syncing.
Response data may be formatted in any way based on method and options by supplying a DataFormatter function.
Use of the syncing feature is advisable when concurrent realtime data is required. For public calls, data is repeatedly requested upon receipt of data; rate is limited automatically. For private calls, rate is managed (according to the rate limit specificiations listed in the Kraken API docs) such that calls may be performed continuously without triggering any rate limit violations.
Syncing is also useful for querying new updates only by modifying the call options upon each response. This can be done within a listener callback provided to the module. Some methods provide a 'since' parameter that can be used for querying only new data. See below for an example.
Additionally, sync instances can be used to store other kinds of data. See below for an example.
Getting Started
Prerequisites
Node.JS version 8.7.0 or above.
Installing
npm i node-kraken-api
Testing
The following command will test the package for errors (using the jest library). Testing may take a few minutes to complete.
Note: In order for authenticated testing to occur, a valid auth.json file must be available in the root directory of the package. Please see the configuration instructions below.
Creating an auth.json file for authenticated testing:
NOTE: Replace 'nano' with your preferred editor.
NOTE: Use a read-only key to be safe.
nano /path/to/node_modules/node-kraken-api/auth.json
Installing the jest library:
npm i --prefix /path/to/node_modules/node-kraken-api jest
Running the testing scripts:
npm test --prefix /path/to/node_modules/node-kraken-api
Deployment
Loading the module:
const kraken = require('node-kraken-api')
Public client instantiation:
const api = kraken()
Private client instantiation (with authenticated configuration):
const api = kraken({
key: '****',
secret: '****',
tier: '****'
})
Or:
NOTE: In this example, OTP is set during instantiation. This is advisable only if the two-factor password for this API key is static. Otherwise, use api.setOTP().
const api = kraken({
key: '****',
secret: '****',
tier: '****',
otp: '****'
})
Instantiation with any number of configuration settings (see configuration):
const api = kraken(require('./config.json'))
Or:
const api = kraken({
key: '****',
secret: '****',
tier: '****',
parse: { dates: false }
})
Usage
Making a single call
Using promises:
api.call('Time')
.then(data => console.log(data))
.catch(err => console.error(err))
Using callbacks:
api.call('Time', (err, data) => {
if (err) console.error(err)
else console.log(data)
})
Using promises (with Kraken method options):
api.call('Depth', { pair: 'XXBTZUSD', count: 1 })
.then(data => console.log(data))
.catch(err => console.error(err))
Using callbacks (with Kraken method options):
api.call('Depth', { pair: 'XXBTZUSD', count: 1 },
(err, data) => {
if (err) console.error(err)
else console.log(data)
}
)
Using a one-time password (if enabled):
NOTE: Due to call queueing functionality and rate limiting, OTP may need to be set again if the call has not been executed before the password decays. This shouldn't be a problem unless there have been a very large volume of calls sent to the queue.
Additionally, depending on settings.retryCt, calls with errors will be re-queued.
As such, it is best to continuously call setOTP with each new password until an error or response has been received.
api.setOTP(158133)
api.call('AddOrder', {
pair: 'XXBTZUSD',
type: 'buy',
price: 5000,
volume: 1
}).then(console.log).catch(console.log)
Custom formatting of response data
Response data may be formatted in any way based on method and options by setting a DataFormatter function during instantiation.
NOTE: Any data returned from this function will be treated as the new data, and call responses will be undefined if it does not return anything.
NOTE: Data formatter is applied to data post-parsing (strings to numbers, strings to dates, etc.), if enabled.
Adding a time formatting rule:
const dataFormatter = (method, options, data) => {
if (method === 'Time') {
return data.unixtime
} else {
return data
}
}
const api = require('node-kraken-api')({ dataFormatter })
api.call('Time').then(
x => console.log(x)
)
Working with data syncing
Creating a sync object:
const timeSync = api.sync('Time')
console.log(timeSync.data)
setTimeout(() => console.log(timeSync.data), 5000)
Creating a sync object with a custom update interval:
const timeSync = api.sync('Time', 5000)
console.log(timeSync.data)
setTimeout(() => console.log(timeSync.data), 10000)
Using syncing promises:
const api = require('./')()
const timeSync = api.sync('Time')
let i = 0
const logUpdates = async () => {
while(i++ < 20) {
try {
console.log(await timeSync.once())
} catch(e) {
console.error(e)
}
}
}
logUpdates()
Creating a sync object (using a listener callback):
const timeSync = api.sync('Time',
(err, data) => {
if (err) console.error(err)
else if (data) console.log(data)
}
)
Adding a listener callback after creation:
const timeSync = api.sync('Time')
timeSync.addListener((err, data) => {
if (err) console.error(err)
else if (data) console.log(data)
})
Adding a once listener via Promises:
const timeSync = api.sync('Time')
timeSync.once()
.then(data => console.log(data))
.catch(err => console.error(err))
Adding a once listener callback:
const timeSync = api.sync('Time')
timeSync.once((err, data) => {
if (err) console.error(err)
else if (data) console.log(data)
})
Closing a sync operation:
const timeSync = api.sync('Time')
timeSync.addListener(
(err, data) => {
if (err) console.error(err)
else if (data) console.log(data)
}
)
setTimeout(timeSync.close, 5000)
Re-opening a sync operation:
const timeSync = api.sync('Time')
timeSync.addListener(
(err, data) => {
if (err) console.error(err)
else if (data) console.log(data)
}
)
setTimeout(timeSync.close, 5000)
setTimeout(timeSync.open, 10000)
Removing a sync listener callback:
const timeSync = api.sync('Time')
const listener = (err, data) => {
if (err) console.error(err)
else if (data) console.log(data)
}
timeSync.addListener(listener)
setTimeout(() => timeSync.removeListener(listener), 5000)
Updating Instance Options
Using a listener within a sync instance may be used for tracking new data only.
For example, methods such as 'Trades' or 'OHLC' respond with a 'last' property to allow for querying new updates.
Logging new trades only:
const tradesSync = api.sync(
'Trades',
{ pair: 'XXBTZUSD' },
(err, data, instance) => {
if (err) {
console.error(err)
} else if (data) {
console.log(data)
instance.options.since = data.last
}
}
)
Custom Handling of Sync Data
Sync object data may be custom tailored for various use cases by using an event listener. Event listeners are provided with a reference to the instance, so they can be defined to transform received data in any way and store it within a custom property.
Creating a realtime historical trades tracker:
const tradesHistory = api.sync(
'Trades',
{ pair: 'XXBTZUSD' },
(err, data, instance) => {
if (data) {
if (!instance.hist) {
instance.hist = []
}
if (data.last !== instance.options.since) {
if (data.XXBTZUSD.forEach) {
data.XXBTZUSD.forEach(
trade => instance.hist.push(trade)
)
}
instance.options.since = data.last
}
}
}
)
tradesHistory.once()
.then(data => console.log(tradesHistory.hist))
.catch(err => console.error(err))
Creating a realtime simple moving average (with safe float operations):
NOTE: OHLC calls are set to a 60s sync interval by default. This may be changed either in the settings configuration, during instance creation, or by changing the instance's interval
property.
const twentyPeriodSMA = api.sync(
'OHLC',
{ pair: 'XXBTZUSD' },
(err, data, instance) => {
if (data) {
if (!instance.bars) instance.bars = []
if (data.last !== instance.options.since) {
if (data.XXBTZUSD.forEach) {
data.XXBTZUSD.forEach(
bar => instance.bars.push(bar)
)
}
instance.options.since = data.last
}
instance.bars = instance.bars.slice(-20)
instance.value = instance.bars.reduce(
(sum, bar) => (
sum + (
(
Math.floor(bar[1] * 100) +
Math.floor(bar[2] * 100) +
Math.floor(bar[3] * 100) +
Math.floor(bar[4] * 100)
) / 4
)
),
0
) / instance.bars.length / 100
}
}
)
twentyPeriodSMA.once()
.then(data => console.log(twentyPeriodSMA.value))
.catch(err => console.error(err))
Configuration
During creation of the API instance, a configuration object may be provided for authenticated calls and other options.
Configuration specifications are detailed in the documentation here
Additionally, various settings may be modified during runtime by using the following functions:
api.setOTP('new2fa')
api.setOTP(232385)
api.setTimeout(10000)
api.setRetryCt(6)
api.setLimiter({ baseIntvl: 1000, minIntvl: 800 })
api.setLimiter({ minIntvl: 500 })
Documentation
Please browse the Kraken API docs for information pertaining to call types and options.
Method names are found within the 'URL' subtitle in the Kraken API docs. For example: 'Get server time', lists the URL as https://api.kraken.com/0/public/Time, which means that the method name is 'Time'.
Alternatively, refer to the default settings in the node-kraken-api documentation. Default method types are listed here (under the 'pubMethods' and 'privMethods' properties).
Method options are found under the 'Input:' section. For example, 'Get order book' lists the following:
pair = asset pair to get market depth for
count = maximum number of asks/bids (optional)
This translates to an object such as { pair: 'XXBTZUSD', count: 10 }
, which should be used when passing method options to API calls.
You may learn more about the types of options and response data by probing the API. Use the methods 'Assets' and 'AssetPairs' to discover the naming scheme for the assets tradable via Kraken.
Internal
Versioning
Versioned using SemVer. For available versions, see the Changelog.
Contribution
Please raise an issue if you find any. Pull requests are welcome!
Author
Justin Collier - jpcx
bitcoin:bc1qla9wynkvmnmcnygls5snqeu3rj5dxr7tunwzp6
Created using npm-kraken-api (nothingisdead) for reference.
License
This project is licensed under the MIT License - see the LICENSE file for details