Files
Superalgos/Server/Exchange/exchangeUtils.js
T

100 lines
2.1 KiB
JavaScript

// generic low level reusuable utils for interacting with exchanges.
const retry = require('retry');
const _ = require('lodash');
const retryInstance = (options, checkFn, callback) => {
if(!options) {
options = {
retries: 100,
factor: 1.2,
minTimeout: 1 * 1000,
maxTimeout: 4 * 1000
};
}
let attempt = 0;
const operation = retry.operation(options);
operation.attempt(function(currentAttempt) {
checkFn((err, result) => {
if (!err) {
return callback(global.DEFAULT_OK_RESPONSE, result);
} else {
if (err.result === global.CUSTOM_OK_RESPONSE.result) {
return callback(err, result);
}
}
let maxAttempts = err.retry;
if(maxAttempts === true)
maxAttempts = 10;
if(err.retry && attempt++ < maxAttempts) {
return operation.retry(err);
}
if(err.notFatal) {
if(err.backoffDelay) {
return setTimeout(() => operation.retry(err), err.backoffDelay);
}
return operation.retry(err);
}
callback(err, result);
});
});
}
const isValidOrder = ({api, market, amount, price}) => {
let reason = false;
// Check amount
if(amount < market.minimalOrder.amount) {
reason = 'Amount is too small';
}
// Some exchanges have restrictions on prices
if(
_.isFunction(api.isValidPrice) &&
!api.isValidPrice(price)
) {
reason = 'Price is not valid';
}
if(
_.isFunction(api.isValidLot) &&
!api.isValidLot(price, amount)
) {
reason = 'Lot size is too small';
}
return {
reason,
valid: !reason
}
}
const includes = function(str, list) {
if (!_.isString(str))
return false;
return _.some(list, item => str.includes(item));
}
const getMarketConfig = function(exchangeProperties) {
return _.find(exchangeProperties.markets, (p) => {
return _.first(p.pair) === global.MARKET.assetA.toUpperCase() &&
_.last(p.pair) === global.MARKET.assetB.toUpperCase();
});
}
module.exports = {
retry: retryInstance,
includes: includes,
isValidOrder,
getMarketConfig
}