Skip to content
This repository has been archived by the owner on Feb 16, 2020. It is now read-only.

Commit

Permalink
init kraken refactor for GB
Browse files Browse the repository at this point in the history
  • Loading branch information
askmike committed Jul 4, 2018
1 parent c8c458f commit 0ea9f12
Show file tree
Hide file tree
Showing 2 changed files with 86 additions and 95 deletions.
174 changes: 81 additions & 93 deletions exchange/wrappers/kraken.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
const Kraken = require('kraken-api');
const moment = require('moment');
const _ = require('lodash');
const retry = require('../exchangeUtils').retry;

const util = require('../core/util');
const Errors = require('../core/error');
const log = require('../core/log');
const marketData = require('./kraken-markets.json');

var Trader = function(config) {
const Trader = function(config) {
_.bindAll(this);

if(_.isObject(config)) {
Expand All @@ -32,52 +30,44 @@ var Trader = function(config) {
);
}

var retryCritical = {
retries: 10,
factor: 1.2,
minTimeout: 1 * 1000,
maxTimeout: 30 * 1000
};

var retryForever = {
forever: true,
factor: 1.2,
minTimeout: 10 * 1000,
maxTimeout: 30 * 1000
};

var recoverableErrors = new RegExp(/(SOCKETTIMEDOUT|TIMEDOUT|CONNRESET|CONNREFUSED|NOTFOUND|API:Rate limit exceeded|API:Invalid nonce|Service:Unavailable|Request timed out|Response code 5)/)

Trader.prototype.processError = function(funcName, error) {
if (!error) return undefined;

if (!error.message.match(recoverableErrors)) {
log.error(`[kraken.js] (${funcName}) returned an irrecoverable error: ${error.message}`);
return new Errors.AbortError('[kraken.js] ' + error.message);
}

log.debug(`[kraken.js] (${funcName}) returned an error, retrying: ${error.message}`);
return new Errors.RetryError('[kraken.js] ' + error.message);
};
const recoverableErrors = [
'SOCKETTIMEDOUT',
'TIMEDOUT',
'CONNRESET',
'CONNREFUSED',
'NOTFOUND',
'API:Rate limit exceeded',
'Service:Unavailable',
'Request timed out',
'Response code 5',
'Empty response'
];

Trader.prototype.handleResponse = function(funcName, callback) {
return (error, body) => {
if(!error) {
if(_.isEmpty(body) || !body.result)
error = new Error('NO DATA WAS RETURNED');

else if(!_.isEmpty(body.error))
error = new Error(body.error);
if(!error && !body) {
error = new Error('Empty response');
}

return callback(this.processError(funcName, error), body);
if(error) {
console.log('error!', error);
if(includes(error.message, recoverableErrors)) {
console.log('is recoverable!');
error.notFatal = true;
}

return callback(error);
}

return callback(undefined, body);
}
};

Trader.prototype.getTrades = function(since, callback, descending) {
var startTs = since ? moment(since).valueOf() : null;
const startTs = since ? moment(since).valueOf() : null;

var processResults = function(err, trades) {
const handle = function(err, trades) {
if (err) return callback(err);

var parsedTrades = [];
Expand All @@ -99,7 +89,7 @@ Trader.prototype.getTrades = function(since, callback, descending) {
callback(undefined, parsedTrades);
};

let reqData = {
const reqData = {
pair: this.pair
};

Expand All @@ -108,38 +98,37 @@ Trader.prototype.getTrades = function(since, callback, descending) {
reqData.since = startTs * 1000000;
}

let handler = (cb) => this.kraken.api('Trades', reqData, this.handleResponse('getTrades', cb));
util.retryCustom(retryForever, _.bind(handler, this), _.bind(processResults, this));
const fetch = cb => this.kraken.api('Trades', reqData, this.handleResponse('getTrades', cb));
retry(null, fetch, handle);
};

Trader.prototype.getPortfolio = function(callback) {
var setBalance = function(err, data) {
const handle = (err, data) => {
if(err) return callback(err);
log.debug('[kraken.js] entering "setBalance" callback after kraken-api call, data:' , data);

var assetAmount = parseFloat( data.result[this.market.prefixed[1]] );
var currencyAmount = parseFloat( data.result[this.market.prefixed[0]] );
const assetAmount = parseFloat( data.result[this.market.prefixed[1]] );
const currencyAmount = parseFloat( data.result[this.market.prefixed[0]] );

if(!_.isNumber(assetAmount) || _.isNaN(assetAmount)) {
log.error(`Kraken did not return portfolio for ${this.asset}, assuming 0.`);
console.log(`Kraken did not return portfolio for ${this.asset}, assuming 0.`);
assetAmount = 0;
}

if(!_.isNumber(currencyAmount) || _.isNaN(currencyAmount)) {
log.error(`Kraken did not return portfolio for ${this.currency}, assuming 0.`);
console.log(`Kraken did not return portfolio for ${this.currency}, assuming 0.`);
currencyAmount = 0;
}

var portfolio = [
const portfolio = [
{ name: this.asset, amount: assetAmount },
{ name: this.currency, amount: currencyAmount }
];

return callback(undefined, portfolio);
};

let handler = (cb) => this.kraken.api('Balance', {}, this.handleResponse('getPortfolio', cb));
util.retryCustom(retryForever, _.bind(handler, this), _.bind(setBalance, this));
const fetch = cb => this.kraken.api('Balance', {}, this.handleResponse('getPortfolio', cb));
retry(null, fetch, handle);
};

// This assumes that only limit orders are being placed with standard assets pairs
Expand All @@ -151,31 +140,30 @@ Trader.prototype.getFee = function(callback) {
};

Trader.prototype.getTicker = function(callback) {
var setTicker = function(err, data) {
const fetch = (err, data) => {
if (err) return callback(err);

var result = data.result[this.pair];
var ticker = {
const result = data.result[this.pair];
const ticker = {
ask: result.a[0],
bid: result.b[0]
};
callback(undefined, ticker);
};

let reqData = {pair: this.pair}

let handler = (cb) => this.kraken.api('Ticker', reqData, this.handleResponse('getTicker', cb));
util.retryCustom(retryForever, _.bind(handler, this), _.bind(setTicker, this));
const reqData = {pair: this.pair}
const fetch = cb => this.kraken.api('Ticker', reqData, this.handleResponse('getTicker', cb));
retry(null, fetch, handle);
};

Trader.prototype.roundAmount = function(amount) {
// Prevent "You incorrectly entered one of fields."
// because of more than 8 decimals.
// Specific precision by pair https://blog.kraken.com/post/1278/announcement-reducing-price-precision-round-2

var precision = 100000000;
var parent = this;
var market = Trader.getCapabilities().markets.find(function(market){ return market.pair[0] === parent.currency && market.pair[1] === parent.asset });
let precision = 100000000;
const parent = this;
const market = Trader.getCapabilities().markets.find(function(market){ return market.pair[0] === parent.currency && market.pair[1] === parent.asset });

if(Number.isInteger(market.precision))
precision = Math.pow(10, market.precision);
Expand All @@ -189,72 +177,72 @@ Trader.prototype.roundAmount = function(amount) {
Trader.prototype.addOrder = function(tradeType, amount, price, callback) {
price = this.roundAmount(price); // only round price, not amount

log.debug('[kraken.js] (addOrder)', tradeType.toUpperCase(), amount, this.asset, '@', price, this.currency);

var setOrder = function(err, data) {
const handle = (err, data) => {
if(err) return callback(err);

var txid = data.result.txid[0];
log.debug('[kraken.js] (addOrder) added order with txid:', txid);
const txid = data.result.txid[0];

callback(undefined, txid);
};

let reqData = {
const reqData = {
pair: this.pair,
type: tradeType.toLowerCase(),
ordertype: 'limit',
price: price,
volume: amount
};

let handler = (cb) => this.kraken.api('AddOrder', reqData, this.handleResponse('addOrder', cb));
util.retryCustom(retryCritical, _.bind(handler, this), _.bind(setOrder, this));
const fetch = cb => this.kraken.api('AddOrder', reqData, this.handleResponse('addOrder', cb));
retry(null, fetch, handle);
};

Trader.prototype.buy = function(amount, price, callback) {
this.addOrder('buy', amount, price, callback);
};

Trader.prototype.sell = function(amount, price, callback) {
this.addOrder('sell', amount, price, callback);
};


Trader.prototype.getOrder = function(order, callback) {
var getOrder = function(err, data) {
const handle = (err, data) => {
if(err) return callback(err);

var price = parseFloat( data.result[ order ].price );
var amount = parseFloat( data.result[ order ].vol_exec );
var date = moment.unix( data.result[ order ].closetm );
const price = parseFloat( data.result[ order ].price );
const amount = parseFloat( data.result[ order ].vol_exec );
const date = moment.unix( data.result[ order ].closetm );

callback(undefined, {price, amount, date});
};

let reqData = {txid: order};
let handler = (cb) => this.kraken.api('QueryOrders', reqData, this.handleResponse('getOrder', cb));
util.retryCustom(retryCritical, _.bind(handler, this), _.bind(getOrder, this));
}
const reqData = {txid: order};

Trader.prototype.buy = function(amount, price, callback) {
this.addOrder('buy', amount, price, callback);
};

Trader.prototype.sell = function(amount, price, callback) {
this.addOrder('sell', amount, price, callback);
};
const fetch = cb => this.kraken.api('QueryOrders', reqData, this.handleResponse('getOrder', cb));
retry(null, fetch, handle);
}

Trader.prototype.checkOrder = function(order, callback) {
var check = function(err, data) {
const handle =(err, data) => {
if(err) return callback(err);

var result = data.result[order];
var stillThere = result.status === 'open' || result.status === 'pending';
const result = data.result[order];
const stillThere = result.status === 'open' || result.status === 'pending';
callback(undefined, !stillThere);
};

let reqData = {txid: order};
let handler = (cb) => this.kraken.api('QueryOrders', reqData, this.handleResponse('checkOrder', cb));
util.retryCustom(retryCritical, _.bind(handler, this), _.bind(check, this));
const reqData = {txid: order};

const fetch = cb => this.kraken.api('QueryOrders', reqData, this.handleResponse('checkOrder', cb));
retry(null, fetch, handle);
};

Trader.prototype.cancelOrder = function(order, callback) {
let reqData = {txid: order};
let handler = (cb) => this.kraken.api('CancelOrder', reqData, this.handleResponse('cancelOrder', cb));
util.retryCustom(retryForever, _.bind(handler, this), callback);
const reqData = {txid: order};

const fetch = cb => this.kraken.api('CancelOrder', reqData, this.handleResponse('cancelOrder', cb));
retry(null, fetch, handle);
};

Trader.getCapabilities = function () {
Expand Down
7 changes: 5 additions & 2 deletions exchange/wrappers/poloniex.js
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ Trader.prototype.processResponse = function(next, fn, payload) {
}

Trader.prototype.findLastOrder = function(since, side, callback) {
this.getOpenOrders((err, result) => {
const handle = (err, result) => {
if(err) {
return callback(err);
}
Expand All @@ -182,7 +182,10 @@ Trader.prototype.findLastOrder = function(since, side, callback) {
}

callback(undefined, order);
});
};

const fetch = next => this.poloniex.getOpenOrders(this.processResponse(next));
retry(null, fetch, handle);
}

Trader.prototype.getOpenOrders = function(callback) {
Expand Down

0 comments on commit 0ea9f12

Please sign in to comment.