Upercase Exchange Folder

This commit is contained in:
mati108@gmail.com committed 2019-02-10 18:08:36 -03:00
1 parent b3da8aba41
commit fd006adc67
9 files changed
+10212

No files matched your search

+301
View File
@@ -0,0 +1,301 @@
exports.newExchangeAPI = function newExchangeAPI(bot, logger) {
/*
This module allows trading bots to connect to the exchange and do trading operations on it. So far it can only work with Poloniex.
*/
const _ = require('lodash')
const isValidOrder = require('./exchangeUtils').isValidOrder
const graphqlClient = require('graphql-client')
const appRoot = require('app-root-path')
const auth = require(`${appRoot}/utils/auth`)
let MODULE_NAME = "Exchange API";
let thisObject = {
initialize: initialize,
getTicker: getTicker,
getOpenPositions: getOpenPositions,
getExecutedTrades: getExecutedTrades,
putPosition: putPosition,
movePosition: movePosition,
getPublicTradeHistory: getPublicTradeHistory,
getExchangeProperties: getExchangeProperties,
getMaxDecimalPositions: getMaxDecimalPositions
};
let apiClient;
return thisObject;
async function initialize(callBackFunction) {
try {
if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] initialize -> Entering function."); }
// TODO integrate with financial beings module to get the exchange
let botExchange = 'Poloniex';
let exchange = botExchange.toLowerCase() + 'Client.js';
let api = require('./wrappers/' + exchange);
const accessToken = await auth.authenticate()
let keyVaultAPI = createKeyVaultAPIClient(accessToken)
apiClient = api.newAPIClient(keyVaultAPI, logger);
callBackFunction(global.DEFAULT_OK_RESPONSE);
} catch (err) {
logger.write(MODULE_NAME, "[ERROR] initialize -> err = " + err.stack);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
}
}
function createKeyVaultAPIClient(authToken) {
if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] createKeyVaultAPIClient -> Entering function."); }
const keyVaultAPI = graphqlClient({
url: global.GATEWAY_ENDPOINT,
headers: {
Authorization: 'Bearer ' + authToken
}
});
keyVaultAPI.signTransaction = function (transaction, next) {
let variables = {
botId: bot.codeName,
transaction: transaction
}
keyVaultAPI.query(`
mutation($botId: String, $transaction: String!){
keyVault_SignTransaction(botId: $botId, transaction: $transaction){
key,
signature,
date
}
}`, variables, function (req, res) {
if (res.status === 401) {
next(undefined, 'Error from graphql: Not authorized');
}
}).then(res => {
if (res.errors) {
next(undefined, 'Error from graphql: ' + res.errors);
} else {
let signature = {
Key: res.data.keyVault_SignTransaction.key,
Sign: res.data.keyVault_SignTransaction.signature
}
next(signature)
}
}).catch(error => {
next(undefined, 'Error signing the message on the key vault: ' + error.message);
});
}
if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] createKeyVaultAPIClient -> Returning graphql client."); }
return keyVaultAPI;
}
/*
* Position Object = {
* id, String
* type, String
* rate, Number
* amountA, Number
* amountB, Number
* fee, Number
* datetime Date
* };
*/
function truncDecimals(pFloatValue) {
let decimals = getMaxDecimalPositions();
return parseFloat(parseFloat(pFloatValue).toFixed(decimals));
}
/*
* Return number of decimals for the current market
*/
function getMaxDecimalPositions() {
return getMarketConfig().maxDecimals;
}
/*
* Return number of decimals for the current market
*/
function getMarketConfig() {
return _.find(getExchangeProperties().markets, (p) => {
return _.first(p.pair) === global.MARKET.assetA.toUpperCase() &&
_.last(p.pair) === global.MARKET.assetB.toUpperCase();
});
}
/*
* Return number of decimals for the current market
*/
function getExchangeProperties() {
try {
if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] getExchangeProperties -> Entering function."); }
return apiClient.getExchangeProperties();
} catch (err) {
logger.write(MODULE_NAME, "[ERROR] getExchangeProperties -> err = " + err.message);
callBack(global.DEFAULT_FAIL_RESPONSE);
}
}
/*
* Returns the price for a given pair of assets.
* The object returned is an array of trades:
* ticker = {
* bid, Number
* ask, Number
* last Number
* };
*/
function getTicker(pMarket, callBack) {
try {
if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] getTicker -> Entering function."); }
apiClient.getTicker(pMarket, callBack);
} catch (err) {
logger.write(MODULE_NAME, "[ERROR] getTicker -> err = " + err.message);
callBack(global.DEFAULT_FAIL_RESPONSE);
}
}
/*
* Returns the open positions ath the exchange for a given market and user account.
* The object returned is an array of positions
*
*/
function getOpenPositions(pMarket, callBack) {
try {
if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] getOpenPositions -> Entering function."); }
if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] getOpenPositions -> pMarket = " + JSON.stringify(pMarket)); }
apiClient.getOpenPositions(pMarket, callBack);
} catch (err) {
logger.write(MODULE_NAME, "[ERROR] getOpenPositions -> Error = " + err.message);
callBack(global.DEFAULT_FAIL_RESPONSE);
}
}
/*
* Returns the trades for a given order number.
* The object returned is an array of positions
*/
function getExecutedTrades(pPositionId, callBack) {
try {
if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] getExecutedTrades -> Entering function."); }
if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] getExecutedTrades -> pPositionId = " + pPositionId); }
apiClient.getExecutedTrades(pPositionId, callBack);
} catch (err) {
logger.write(MODULE_NAME, "[ERROR] getExecutedTrades -> Error = " + err.message);
callBack(global.DEFAULT_FAIL_RESPONSE);
}
}
/*
* Creates a new buy or sell order.
* The orderNumber is returned. String
*/
function putPosition(pMarket, pType, pRate, pAmountA, pAmountB, callBack) {
try {
if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] putPosition -> Entering function."); }
if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] putPosition -> pMarket = " + JSON.stringify(pMarket)); }
if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] putPosition -> pType = " + pType); }
if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] putPosition -> pRate = " + truncDecimals(pRate)); }
if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] putPosition -> pAmountA = " + truncDecimals(pAmountA)); }
if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] putPosition -> pAmountB = " + truncDecimals(pAmountB)); }
let check = isValidOrder({
market: getMarketConfig(),
api: apiClient,
amount: truncDecimals(pAmountB),
price: truncDecimals(pRate)
});
if (check.valid) {
if (pType === "buy") {
apiClient.buy(pMarket.assetA, pMarket.assetB, truncDecimals(pRate), truncDecimals(pAmountB), callBack);
return;
}
if (pType === "sell") {
apiClient.sell(pMarket.assetA, pMarket.assetB, truncDecimals(pRate), truncDecimals(pAmountB), callBack);
return;
}
logger.write(MODULE_NAME, "[ERROR] putPosition -> pType must be either 'buy' or 'sell'.");
callBack(global.DEFAULT_FAIL_RESPONSE);
} else {
logger.write(MODULE_NAME, "[ERROR] putPosition -> The order is invalid: " + check.reason);
callBack(global.DEFAULT_FAIL_RESPONSE);
}
} catch (err) {
logger.write(MODULE_NAME, "[ERROR] putPosition -> err = " + err.message);
callBack(global.DEFAULT_FAIL_RESPONSE);
}
}
/*
* Move an existing position to the new rate.
* The new orderNumber is returned.
*/
function movePosition(pPosition, pNewRate, pNewAmountB, callBack) {
try {
if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] movePosition -> Entering function."); }
if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] movePosition -> pPosition = " + JSON.stringify(pPosition)); }
if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] movePosition -> pNewRate = " + truncDecimals(pNewRate)); }
apiClient.movePosition(pPosition, truncDecimals(pNewRate), truncDecimals(pNewAmountB), callBack);
} catch (err) {
logger.write(MODULE_NAME, "[ERROR] movePosition -> err = " + err.message);
callBack(global.DEFAULT_FAIL_RESPONSE);
}
}
/*
* Returns all the trade history from the Exchange since startTime to endTime orderd by tradeId.
* It's possible that the exchange doesn't support this method.
* The object returned is an array of trades:
* trade = {
* tradeID, String
* globalTradeID, String
* type, String
* rate, Number
* amountA, Number
* amountB, Number
* date Date
* };
*/
function getPublicTradeHistory(assetA, assetB, startTime, endTime, callBack) {
try {
if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] getTradeHistory -> Entering function."); }
apiClient.getPublicTradeHistory(assetA, assetB, startTime, endTime, callBack);
} catch (err) {
logger.write(MODULE_NAME, "[ERROR] getTradeHistory -> err = " + err.message);
callBack(global.DEFAULT_FAIL_RESPONSE);
}
}
};
+100
View File
@@ -0,0 +1,100 @@
// 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
}
+183
View File
@@ -0,0 +1,183 @@
const _ = require('underscore');
class Beautifier {
constructor() {
this._beautifications = {
aggTrades: {
a: 'aggTradeId',
p: 'price',
q: 'quantity',
f: 'firstTradeId',
l: 'lastTradeId',
T: 'timestamp',
m: 'maker',
M: 'bestPriceMatch'
},
klines: {
0: 'openTime',
1: 'open',
2: 'high',
3: 'low',
4: 'close',
5: 'volume',
6: 'closeTime',
7: 'quoteAssetVolume',
8: 'trades',
9: 'takerBaseAssetVolume',
10: 'takerQuoteAssetVolume',
11: 'ignored'
},
bids: [
{
0: 'price',
1: 'quantity',
2: 'ignored'
}
],
asks: [
{
0: 'price',
1: 'quantity',
2: 'ignored'
}
],
depthUpdateEvent: {
e: 'eventType',
E: 'eventTime',
s: 'symbol',
u: 'updateId',
b: 'bidDepthDelta',
a: 'askDepthDelta'
},
bidDepthDelta: [
{
0: 'price',
1: 'quantity',
2: 'ignored'
}
],
askDepthDelta: [
{
0: 'price',
1: 'quantity',
2: 'ignored'
}
],
klineEvent: {
e: 'eventType',
E: 'eventTime',
s: 'symbol',
k: 'kline'
},
kline: {
t: 'startTime',
T: 'endTime',
s: 'symbol',
i: 'interval',
f: 'firstTradeId',
L: 'lastTradeId',
o: 'open',
c: 'close',
h: 'high',
l: 'low',
v: 'volume',
n: 'trades',
x: 'final',
q: 'quoteVolume',
V: 'volumeActive',
Q: 'quoteVolumeActive',
B: 'ignored'
},
aggTradeEvent: {
e: 'eventType',
E: 'eventTime',
s: 'symbol',
a: 'tradeId',
p: 'price',
q: 'quantity',
f: 'firstTradeId',
l: 'lastTradeId',
T: 'time',
m: 'maker',
M: 'ignored'
},
outboundAccountInfoEvent: {
e: 'eventType',
E: 'eventTime',
m: 'makerCommission',
t: 'takerCommission',
b: 'buyerCommission',
s: 'sellerCommission',
T: 'canTrade',
W: 'canWithdraw',
D: 'canDeposit',
B: 'balances'
},
balances: [
{
a: 'asset',
f: 'availableBalance',
l: 'onOrderBalance'
}
],
executionReportEvent: {
e: 'eventType',
E: 'eventTime',
s: 'symbol',
c: 'newClientOrderId',
S: 'side',
o: 'orderType',
f: 'cancelType', // GTC 'Good till Cancel', IOC: 'Immediate or Cancel'
q: 'quantity',
p: 'price',
x: 'executionType',
X: 'orderStatus',
r: 'rejectReason',
i: 'orderId',
l: 'lastTradeQuantity',
z: 'accumulatedQuantity',
L: 'lastTradePrice',
n: 'commission',
N: 'commissionAsset',
m: 'maker',
T: 'tradeTime',
t: 'tradeId'
}
}
}
beautify(object, type) {
const beautifications = _.isObject(type) ? type : this._beautifications[type];
if (!beautifications) {
return object;
}
let newItem = {};
_.each(object, (value, key) => {
if (_.isArray(value)) {
const newArray = [];
if (_.isArray(this._beautifications[beautifications[key]])) {
_.each(value, arrayValue => {
newArray.push(this.beautify(arrayValue, this._beautifications[beautifications[key]][0]));
});
} else {
_.each(value, (value, key) => {
newItem.push(this.beautify(value, beautifications[key]))
});
}
const newKey = beautifications[key] || key;
newItem[newKey] = newArray;
} else if (_.isObject(value)) {
const newKey = beautifications[key] || key;
newItem[newKey] = this.beautify(value, beautifications[key]);
} else {
const newKey = beautifications[key] || key;
newItem[newKey] = value;
}
});
return newItem;
}
}
module.exports = Beautifier;
+368
View File
@@ -0,0 +1,368 @@
// Source: https://github.com/zoeyg/binance/blob/master/lib/rest.js
const request = require('request');
const qs = require('querystring');
const _ = require('underscore');
const crypto = require('crypto');
const Beautifier = require('./beautifier.js');
const assert = require('assert');
class BinanceRest {
constructor({
key,
secret,
recvWindow = false,
timeout = 15000,
disableBeautification = false,
handleDrift = false
}) {
this.key = key;
this.secret = secret;
this.recvWindow = recvWindow;
this.timeout = timeout;
this.disableBeautification = disableBeautification;
this.handleDrift = handleDrift;
this._beautifier = new Beautifier();
this._baseUrl = 'https://api.binance.com/';
this._drift = 0;
this._syncInterval = 0;
}
_makeRequest(query, callback, route, security, method, attempt = 0) {
assert(_.isUndefined(callback) || _.isFunction(callback), 'callback must be a function or undefined');
assert(_.isObject(query), 'query must be an object');
let queryString;
const type = _.last(route.split('/')),
options = {
url: `${this._baseUrl}${route}`,
timeout: this.timeout
};
if (security === 'SIGNED') {
if (this.recvWindow) {
query.recvWindow = this.recvWindow;
}
queryString = qs.stringify(query);
options.url += '?' + queryString;
if (options.url.substr(options.url.length - 1) !== '?') {
options.url += '&';
}
options.url += `signature=${this._sign(queryString)}`;
} else {
queryString = qs.stringify(query);
if (queryString) {
options.url += '?' + queryString;
}
}
if (security === 'API-KEY' || security === 'SIGNED') {
options.headers = { 'X-MBX-APIKEY': this.key };
}
if (method) {
options.method = method;
}
const action = cb => {
request(options, (err, response, body) => {
let payload;
try {
payload = JSON.parse(body);
} catch (e) {
payload = body;
}
if (err) {
cb(err, payload);
} else if (response.statusCode < 200 || response.statusCode > 299) {
/*
* If we get a response that the timestamp is ahead of the server,
* calculate the drift and then attempt the request again
*/
if (response.statusCode === 400 && payload.code === -1021 &&
this.handleDrift && attempt === 0) {
this.calculateDrift()
.then(() => {
query.timestamp = this._getTime() + this._drift;
return this._makeRequest(query, cb, route, security, method,
++attempt);
});
} else {
cb(new Error(`Response code ${response.statusCode}`), payload);
}
} else {
if (_.isArray(payload)) {
payload = _.map(payload, (item) => {
return this._doBeautifications(item, type);
});
} else {
payload = this._doBeautifications(payload);
}
cb(err, payload);
}
});
};
if (callback) {
action(callback);
} else {
return new Promise((resolve, reject) => action((err, payload) => {
if (err) {
if (payload === undefined) {
reject(err);
} else {
reject(payload);
}
} else {
resolve(payload);
}
}));
}
}
_doBeautifications(response, route) {
if (this.disableBeautification) {
return response;
}
return this._beautifier.beautify(response, route);
}
_sign(queryString) {
return crypto.createHmac('sha256', this.secret)
.update(queryString)
.digest('hex');
}
_setTimestamp(query) {
if (!query.timestamp) {
query.timestamp = this._getTime() + this._drift;
}
}
_getTime() {
return new Date().getTime();
}
calculateDrift() {
const systemTime = this._getTime();
return this.time()
.then((response) => {
// Calculate the approximate trip time from here to binance
const transitTime = parseInt((this._getTime() - systemTime) / 2);
this._drift = response.serverTime - (systemTime + transitTime);
});
}
startTimeSync(interval = 300000, onRecalculateDriftCb) {
return new Promise((resolve, reject) => {
// If there's already an interval running, clear it and reset values
if (this._syncInterval !== 0) {
this.endTimeSync();
return resolve();
}
// Calculate initial drift value and setup interval to periodically update it
this.calculateDrift()
.then(resolve)
.catch(reject);
this._syncInterval = setInterval(() => {
const promise = this.calculateDrift();
if (_.isFunction(onRecalculateDriftCb)) {
onRecalculateDriftCb(promise);
}
}, interval);
});
}
endTimeSync() {
clearInterval(this._syncInterval);
this._drift = 0;
this._syncInterval = 0;
}
// Public APIs
ping(callback) {
return this._makeRequest({}, callback, 'api/v1/ping');
}
time(callback) {
return this._makeRequest({}, callback, 'api/v1/time');
}
depth(query = {}, callback) {
if (_.isString(query)) {
query = { symbol: query };
}
return this._makeRequest(query, callback, 'api/v1/depth');
}
trades(query = {}, callback) {
if (_.isString(query)) {
query = { symbol: query };
}
return this._makeRequest(query, callback, 'api/v1/trades');
}
historicalTrades(query = {}, callback) {
if (_.isString(query)) {
query = { symbol: query };
}
return this._makeRequest(query, callback, 'api/v1/historicalTrades', 'API-KEY');
}
aggTrades(query = {}, callback) {
if (_.isString(query)) {
query = { symbol: query };
}
return this._makeRequest(query, callback, 'api/v1/aggTrades');
}
exchangeInfo(callback) {
return this._makeRequest({}, callback, 'api/v1/exchangeInfo');
}
klines(query = {}, callback) {
return this._makeRequest(query, callback, 'api/v1/klines');
}
ticker24hr(query = {}, callback) {
if (_.isString(query)) {
query = { symbol: query };
}
return this._makeRequest(query, callback, 'api/v1/ticker/24hr');
}
tickerPrice(query = {}, callback) {
if (_.isString(query)) {
query = { symbol: query };
}
return this._makeRequest(query, callback, 'api/v3/ticker/price');
}
bookTicker(query = {}, callback) {
if (_.isString(query)) {
query = { symbol: query };
}
return this._makeRequest(query, callback, 'api/v3/ticker/bookTicker');
}
allBookTickers(callback) {
return this._makeRequest({}, callback, 'api/v1/ticker/allBookTickers');
}
allPrices(callback) {
return this._makeRequest({}, callback, 'api/v1/ticker/allPrices');
}
// Private APIs
newOrder(query = {}, callback) {
this._setTimestamp(query);
return this._makeRequest(query, callback, 'api/v3/order', 'SIGNED', 'POST');
}
testOrder(query = {}, callback) {
this._setTimestamp(query);
return this._makeRequest(query, callback, 'api/v3/order/test', 'SIGNED', 'POST');
}
queryOrder(query = {}, callback) {
this._setTimestamp(query);
return this._makeRequest(query, callback, 'api/v3/order', 'SIGNED');
}
cancelOrder(query = {}, callback) {
this._setTimestamp(query);
return this._makeRequest(query, callback, 'api/v3/order', 'SIGNED', 'DELETE');
}
openOrders(query = {}, callback) {
if (_.isString(query)) {
query = { symbol: query };
}
this._setTimestamp(query);
return this._makeRequest(query, callback, 'api/v3/openOrders', 'SIGNED');
}
allOrders(query = {}, callback) {
if (_.isString(query)) {
query = { symbol: query };
}
this._setTimestamp(query);
return this._makeRequest(query, callback, 'api/v3/allOrders', 'SIGNED');
}
account(query = {}, callback) {
if (_.isFunction(query)) {
callback = query;
query = {};
}
this._setTimestamp(query);
return this._makeRequest(query, callback, 'api/v3/account', 'SIGNED');
}
myTrades(query = {}, callback) {
if (_.isString(query)) {
query = { symbol: query };
}
this._setTimestamp(query);
return this._makeRequest(query, callback, 'api/v3/myTrades', 'SIGNED');
}
withdraw(query = {}, callback) {
this._setTimestamp(query);
return this._makeRequest(query, callback, 'wapi/v3/withdraw.html', 'SIGNED', 'POST');
}
depositHistory(query = {}, callback) {
if (_.isString(query)) {
query = { asset: query };
}
this._setTimestamp(query);
return this._makeRequest(query, callback, 'wapi/v3/depositHistory.html', 'SIGNED');
}
withdrawHistory(query = {}, callback) {
if (_.isString(query)) {
query = { asset: query };
}
this._setTimestamp(query);
return this._makeRequest(query, callback, 'wapi/v3/withdrawHistory.html', 'SIGNED');
}
depositAddress(query = {}, callback) {
if (_.isString(query)) {
query = { asset: query };
}
this._setTimestamp(query);
return this._makeRequest(query, callback, 'wapi/v3/depositAddress.html', 'SIGNED');
}
accountStatus(callback) {
const query = {};
this._setTimestamp(query);
return this._makeRequest(query, callback, 'wapi/v3/accountStatus.html', 'SIGNED');
}
startUserDataStream(callback) {
return this._makeRequest({}, callback, 'api/v1/userDataStream', 'API-KEY', 'POST');
}
keepAliveUserDataStream(query = {}, callback) {
return this._makeRequest(query, callback, 'api/v1/userDataStream', 'API-KEY', 'PUT');
}
closeUserDataStream(query = {}, callback) {
return this._makeRequest(query, callback, 'api/v1/userDataStream', 'API-KEY', 'DELETE');
}
}
module.exports = BinanceRest;
+360
View File
@@ -0,0 +1,360 @@
exports.newAPIClient = function newAPIClient(pKey, pSecret, logger) {
/*
/* ATENTION: This Library is used both a the cloud (AACloud) and also at the AAWeb on the server side without any modifications.
/* There is a second version, stripped of all the inner functionality that runs at AAWeb browser side, which channels the requests to the server side one.
*/
const FULL_LOG = true;
const LOG_FILE_CONTENT = true;
const MODULE_NAME = "binanceClient";
const retry = require('../exchangeUtils').retry;
const includes = require('../exchangeUtils').includes;
const _ = require('lodash');
const Binance = require("./binance");
const exchangeProperties = require('./poloniexProperties.json');
const recoverableErrors = [
'SOCKETTIMEDOUT',
'TIMEDOUT',
'CONNRESET',
'CONNREFUSED',
'NOTFOUND',
'Error -1021',
'Response code 429',
'Response code 5',
'ETIMEDOUT'
];
let API = new Binance({
key: pKey,
secret: pSecret,
timeout: 15000,
recvWindow: 60000,
disableBeautification: false,
handleDrift: true,
});
let thisObject = {
getTicker: getTicker,
getOpenPositions: getOpenPositions,
getExecutedTrades: getExecutedTrades,
buy: buy,
sell: sell,
movePosition: movePosition,
getPublicTradeHistory: getPublicTradeHistory,
getExchangeProperties: getExchangeProperties,
isValidPrice: isValidPrice,
isValidLot: isValidLot
};
return thisObject;
function joinCurrencies(currencyA, currencyB) {
// If only one arg, then return the first
if (typeof currencyB !== 'string') {
return currencyA;
}
return currencyB + '' + currencyA;
}
/*
* Returns the properties for this exchange
*/
function getExchangeProperties() {
return exchangeProperties;
}
function truncDecimals(pFloatValue) {
let decimals = 6;
return parseFloat(parseFloat(pFloatValue).toFixed(decimals));
}
/*
* Returns the price for a given pair of assets.
* The object returned is an array of trades:
* ticker = {
* bid, Number
* ask, Number
* last Number
* };
*/
function getTicker(pMarket, callBack) {
let market = joinCurrencies(pMarket.assetA, pMarket.assetB);
const handle = (err, response) => {
let ticker;
if (err.result === global.DEFAULT_OK_RESPONSE.result) {
ticker = {
bid: Number(response.bidPrice),
ask: Number(response.askPrice),
last: Number(response.lastPrice)
};
}
callBack(err, ticker);
};
const fetch = next => API.ticker24hr(market, analizeResponse(next));
retry(null, fetch, handle);
}
/*
* Returns the open positions ath the exchange for a given market and user account.
* The object returned is an array of positions:
* position = {
* id,
* type,
* rate, Number
* amountA, Number
* amountB, Number
* fee, (not required) Number
* datetime
* };
*/
function getOpenPositions(pMarket, callBack) {
let market = joinCurrencies(pMarket.assetA, pMarket.assetB);
const handle = (err, response) => {
let exchangePositions = [];
if (err.result === global.DEFAULT_OK_RESPONSE.result) {
for (let i = 0; i < response.length; i++) {
let openPosition = {
id: response[i].orderId,
type: response[i].side.toLowerCase(),
rate: Number(response[i].price),
amountA: truncDecimals(response[i].origQty * response[i].price),
amountB: Number(response[i].origQty),
date: (new Date(response[i].time)).valueOf()
};
exchangePositions.push(openPosition);
}
}
callBack(err, exchangePositions);
};
const fetch = next => API.openOrders(market, analizeResponse(next));
retry(null, fetch, handle);
}
/*
* Returns the trades for a given order number.
* The object returned is an array of positions:
* position = {
* id,
* type,
* rate, Number
* amountA, Number
* amountB, Number
* fee, Number
* datetime
* };
*/
function getExecutedTrades(pPositionId, callBack) {
let market = joinCurrencies(global.MARKET.assetA, global.MARKET.assetB);
const handle = (err, response) => {
let exchangeTrades = [];
if (err.result === global.DEFAULT_OK_RESPONSE.result) {
for (let i = 0; i < response.length; i++) {
if (response[i].orderId === pPositionId) {
let type = "sell"
if (response[i].isBuyer === true) {
type = "buy";
}
let trade = {
id: response[i].orderId,
type: type,
rate: Number(response[i].price),
amountA: truncDecimals(response[i].qty * response[i].price),
amountB: Number(response[i].qty),
fee: Number(response[i].commission),
date: (new Date(response[i].time)).valueOf()
};
exchangeTrades.push(trade);
break;
}
}
}
callBack(err, exchangeTrades);
};
const fetch = next => API.myTrades(market, analizeResponse(next));
retry(null, fetch, handle);
}
/*
* Creates a new buy order.
* The orderNumber is returned.
*/
function buy(pCurrencyA, pCurrencyB, pRate, pAmount, callBack) {
var parameters = {
symbol: joinCurrencies(pCurrencyA, pCurrencyB),
side: 'BUY',
type: 'LIMIT',
quantity: pAmount,
price: pRate,
timeInForce: 'GTC'
};
const handle = (err, response) => {
let orderNumber;
if (err.result === global.DEFAULT_OK_RESPONSE.result) {
orderNumber = response.orderId;
}
callBack(err, orderNumber);
};
const fetch = next => API.newOrder(parameters, analizeResponse(next));
retry(null, fetch, handle);
}
/*
* Creates a new sell order.
* The orderNumber is returned. String
*/
function sell(pCurrencyA, pCurrencyB, pRate, pAmount, callBack) {
var parameters = {
symbol: joinCurrencies(pCurrencyA, pCurrencyB),
side: 'SELL',
type: 'LIMIT',
quantity: pAmount,
price: pRate,
timeInForce: 'GTC'
};
const handle = (err, response) => {
let orderNumber;
if (err.result === global.DEFAULT_OK_RESPONSE.result) {
orderNumber = response.orderId;
}
callBack(err, orderNumber);
};
const fetch = next => API.newOrder(parameters, analizeResponse(next));
retry(null, fetch, handle);
}
/* //TODO Complete partial orders
* Move an existing position to the new rate.
* The new orderNumber is returned. String
*/
function movePosition(pPosition, pNewRate, pNewAmountB, callBack) {
var parameters = {
symbol: joinCurrencies(global.MARKET.assetA, global.MARKET.assetB),
orderId: pPosition.id
};
if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] moveOrder -> Step1 - Cancelling Position: " + JSON.stringify(pPosition)); }
const handle = (err, response) => {
if (err.result === global.DEFAULT_OK_RESPONSE.result) {
if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] moveOrder -> Step1 Complete - Position Cancelled. Proceeding to put the new position."); }
if (position.type === "buy")
thisObject.buy(global.MARKET.assetA, global.MARKET.assetB, pNewRate, pNewAmountB, callBack);
else
thisObject.sell(global.MARKET.assetA, global.MARKET.assetB, pNewRate, pNewAmountB, callBack);
} else {
callBack(err);
}
};
const fetch = next => API.cancelOrder(parameters, analizeResponse(next));
retry(null, fetch, handle);
}
/* //TODO Pending Review
* Returns all the trade history from the Exchange since startTime to endTime orderd by tradeId.
* It's possible that the exchange doesn't support this method.
* The object returned is an array of trades:
* trade = {
* tradeIdAtExchange,
* marketIdAtExchange,
* type,
* rate, Number
* amountA, Number
* amountB, Number
* datetime
* };
*/
function getPublicTradeHistory(assetA, assetB, startTime, endTime, callBack) {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] getPublicTradeHistory -> Not implemented yet."); }
callBack(global.DEFAULT_FAIL_RESPONSE);
}
function isValidPrice(price) {
return price >= market.minimalOrder.price;
}
function isValidLot(price, amount) {
return amount * price >= market.minimalOrder.order;
}
function analizeResponse(callBack) {
return (exchangeErr, exchangeResponse) => {
let error;
/* This function analizes the different situations we might encounter trying to access Poloniex and returns appropiate standard errors. */
let stringExchangeErr = JSON.stringify(exchangeErr);
let stringExchangeResponse = JSON.stringify(exchangeResponse);
try {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] analizeResponse -> exchangeErr = " + stringExchangeErr); }
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] analizeResponse -> exchangeResponse = " + stringExchangeResponse); }
if (exchangeErr) {
error = global.DEFAULT_FAIL_RESPONSE;
} else if (!exchangeResponse) {
error = {
result: global.DEFAULT_FAIL_RESPONSE.result,
message: 'Empty response'
};
} else if (includes(exchangeResponse.error, ['<!DOCTYPE html>'])) {
error = {
result: global.DEFAULT_FAIL_RESPONSE.result,
message: exchangeResponse
};
} else if (includes(exchangeResponse.error, ['Order does not exist.'])) {
error = {
result: global.CUSTOM_OK_RESPONSE.result,
message: exchangeResponse.error
};
} else if (exchangeResponse.error) {
error = {
result: global.DEFAULT_FAIL_RESPONSE.result,
message: exchangeResponse.error
};
}
if (error) {
if (includes(error.message, recoverableErrors)) {
error.notFatal = true;
}
}
return callBack(error, exchangeResponse);
} catch (err) {
logger.write(MODULE_NAME, "[ERROR] analizeResponse -> err.message = " + err.message);
return callBack(global.DEFAULT_FAIL_RESPONSE, exchangeResponse);
}
}
}
}
File diff suppressed because it is too large. Load diff
+440
View File
@@ -0,0 +1,440 @@
// Source: https://github.com/premasagar/poloniex.js
// Documentation: https://poloniex.com/support/api/
module.exports = (function () {
'use strict';
// Module dependencies
var request = require('request'),
nonce = require('nonce')();
// Constants
var version = '0.0.9',
PUBLIC_API_URL = 'https://poloniex.com/public',
PRIVATE_API_URL = 'https://poloniex.com/tradingApi',
USER_AGENT = 'poloniex.js ' + version;
//USER_AGENT = 'Mozilla/5.0 (Windows NT 6.3; WOW64; rv:26.0) Gecko/20100101 Firefox/26.0'
// Helper methods
function joinCurrencies(currencyA, currencyB) {
// If only one arg, then return the first
if (typeof currencyB !== 'string') {
return currencyA;
}
return currencyA + '_' + currencyB;
}
// Constructor
function Poloniex(keyVaultAPI) {
// Generate headers signed by this user's key and secret.
// The secret is encapsulated and never exposed
this._getPrivateHeaders = function (parameters, next) {
var paramString, signature;
// Convert to `arg1=foo&arg2=bar`
paramString = Object.keys(parameters).map(function (param) {
return encodeURIComponent(param) + '=' + encodeURIComponent(parameters[param]);
}).join('&');
keyVaultAPI.signTransaction(paramString, next);
};
}
// Currently, this fails with `Error: CERT_UNTRUSTED`
// Poloniex.STRICT_SSL can be set to `false` to avoid this. Use with caution.
// Will be removed in future, once this is resolved.
Poloniex.STRICT_SSL = true;
// Customisable user agent string
Poloniex.USER_AGENT = USER_AGENT;
// Prototype
Poloniex.prototype = {
constructor: Poloniex,
// Make an API request
_request: function (options, callback) {
if (!('headers' in options)) {
options.headers = {};
}
options.json = true;
options.headers['User-Agent'] = Poloniex.USER_AGENT;
options.strictSSL = Poloniex.STRICT_SSL;
options.timeout = 60 * 1000;
request(options, function (err, response, body) {
// Empty response
if (!err && (typeof body === 'undefined' || body === null)) {
err = 'Empty response';
}
callback(err, body);
});
return this;
},
// Make a public API request
_public: function (command, parameters, callback) {
var options;
if (typeof parameters === 'function') {
callback = parameters;
parameters = {};
}
parameters || (parameters = {});
parameters.command = command;
options = {
method: 'GET',
url: PUBLIC_API_URL,
qs: parameters
};
options.qs.command = command;
return this._request(options, callback);
},
// Make a private API request
_private: function (command, parameters, callback) {
var options;
if (typeof parameters === 'function') {
callback = parameters;
parameters = {};
}
parameters || (parameters = {});
parameters.command = command;
parameters.nonce = nonce();
options = {
method: 'POST',
url: PRIVATE_API_URL,
form: parameters
};
let headers = this._getPrivateHeaders(parameters, next)
let thisObject = this
function next(pHeaders, error) {
if (!error) {
options.headers = pHeaders;
return thisObject._request(options, callback);
} else {
callback(error);
}
}
},
/////
// PUBLIC METHODS
returnTicker: function (callback) {
return this._public('returnTicker', callback);
},
return24hVolume: function (callback) {
return this._public('return24hVolume', callback);
},
returnOrderBook: function (currencyA, currencyB, callback) {
var currencyPair;
if (typeof currencyB === 'function') {
currencyPair = currencyA;
callback = currencyB;
currencyB = null;
}
else {
currencyPair = joinCurrencies(currencyA, currencyB);
}
var parameters = {
currencyPair: currencyPair
};
return this._public('returnOrderBook', parameters, callback);
},
returnChartData: function (currencyA, currencyB, period, start, end, callback) {
var parameters = {
currencyPair: joinCurrencies(currencyA, currencyB),
period: period,
start: start,
end: end
};
return this._public('returnChartData', parameters, callback);
},
returnCurrencies: function (callback) {
return this._public('returnCurrencies', callback);
},
returnLoanOrders: function (currency, callback) {
return this._public('returnLoanOrders', { currency: currency }, callback);
},
returnTradeHistory: function (currencyA, currencyB, start, end, callback) {
if (arguments.length < 5) {
callback = start;
start = Date.now() / 1000 - 60 * 60;
end = Date.now() / 1000 + 60 * 60; // Some buffer in case of client/server time out of sync.
}
var parameters = {
currencyPair: joinCurrencies(currencyA, currencyB),
start: start,
end: end
};
return this._public('returnTradeHistory', parameters, callback);
},
/////
// PRIVATE METHODS
returnBalances: function (callback) {
return this._private('returnBalances', {}, callback);
},
returnCompleteBalances: function (account, callback) {
var parameters = {};
if (typeof account === 'function') {
callback = account;
}
else if (typeof account === 'string' && !!account) {
parameters.account = account;
}
return this._private('returnCompleteBalances', parameters, callback);
},
returnDepositAddresses: function (callback) {
return this._private('returnDepositAddresses', {}, callback);
},
generateNewAddress: function (currency, callback) {
return this._private('generateNewAddress', { currency: currency }, callback);
},
returnDepositsWithdrawals: function (start, end, callback) {
return this._private('returnDepositsWithdrawals', { start: start, end: end }, callback);
},
// can be called with `returnOpenOrders('all', callback)`
returnOpenOrders: function (currencyA, currencyB, callback) {
var currencyPair;
if (typeof currencyB === 'function') {
currencyPair = currencyA;
callback = currencyB;
currencyB = null;
}
else {
currencyPair = joinCurrencies(currencyA, currencyB);
}
var parameters = {
currencyPair: currencyPair
};
return this._private('returnOpenOrders', parameters, callback);
},
returnOrderTrades: function (orderNumber, callback) {
var parameters = {
orderNumber: orderNumber
};
return this._private('returnOrderTrades', parameters, callback);
},
buy: function (currencyA, currencyB, rate, amount, callback) {
var parameters = {
currencyPair: joinCurrencies(currencyA, currencyB),
rate: rate,
amount: amount
};
return this._private('buy', parameters, callback);
},
sell: function (currencyA, currencyB, rate, amount, callback) {
var parameters = {
currencyPair: joinCurrencies(currencyA, currencyB),
rate: rate,
amount: amount
};
return this._private('sell', parameters, callback);
},
cancelOrder: function (currencyA, currencyB, orderNumber, callback) {
var parameters = {
currencyPair: joinCurrencies(currencyA, currencyB),
orderNumber: orderNumber
};
return this._private('cancelOrder', parameters, callback);
},
moveOrder: function (orderNumber, rate, amount, callback) {
var parameters = {
orderNumber: orderNumber,
rate: rate,
amount: amount ? amount : null
};
return this._private('moveOrder', parameters, callback);
},
withdraw: function (currency, amount, address, callback) {
var parameters = {
currency: currency,
amount: amount,
address: address
};
return this._private('withdraw', parameters, callback);
},
returnFeeInfo: function (callback) {
return this._private('returnFeeInfo', {}, callback);
},
returnAvailableAccountBalances: function (account, callback) {
var parameters = {};
if (typeof account === 'function') {
callback = account;
}
else if (typeof account === 'string' && !!account) {
parameters.account = account;
}
return this._private('returnAvailableAccountBalances', parameters, callback);
},
returnTradableBalances: function (callback) {
return this._private('returnTradableBalances', {}, callback);
},
transferBalance: function (currency, amount, fromAccount, toAccount, callback) {
var parameters = {
currency: currency,
amount: amount,
fromAccount: fromAccount,
toAccount: toAccount
};
return this._private('transferBalance', parameters, callback);
},
returnMarginAccountSummary: function (callback) {
return this._private('returnMarginAccountSummary', {}, callback);
},
marginBuy: function (currencyA, currencyB, rate, amount, lendingRate, callback) {
var parameters = {
currencyPair: joinCurrencies(currencyA, currencyB),
rate: rate,
amount: amount,
lendingRate: lendingRate ? lendingRate : null
};
return this._private('marginBuy', parameters, callback);
},
marginSell: function (currencyA, currencyB, rate, amount, lendingRate, callback) {
var parameters = {
currencyPair: joinCurrencies(currencyA, currencyB),
rate: rate,
amount: amount,
lendingRate: lendingRate ? lendingRate : null
};
return this._private('marginSell', parameters, callback);
},
getMarginPosition: function (currencyA, currencyB, callback) {
var parameters = {
currencyPair: joinCurrencies(currencyA, currencyB)
};
return this._private('getMarginPosition', parameters, callback);
},
closeMarginPosition: function (currencyA, currencyB, callback) {
var parameters = {
currencyPair: joinCurrencies(currencyA, currencyB)
};
return this._private('closeMarginPosition', parameters, callback);
},
createLoanOffer: function (currency, amount, duration, autoRenew, lendingRate, callback) {
var parameters = {
currency: currency,
amount: amount,
duration: duration,
autoRenew: autoRenew,
lendingRate: lendingRate
};
return this._private('createLoanOffer', parameters, callback);
},
cancelLoanOffer: function (orderNumber, callback) {
var parameters = {
orderNumber: orderNumber
};
return this._private('cancelLoanOffer', parameters, callback);
},
returnOpenLoanOffers: function (callback) {
return this._private('returnOpenLoanOffers', {}, callback);
},
returnActiveLoans: function (callback) {
return this._private('returnActiveLoans', {}, callback);
},
returnLendingHistory: function (start, end, limit, callback) {
var parameters = {
start: start,
end: end,
limit: limit
};
return this._private('returnLendingHistory', parameters, callback);
},
toggleAutoRenew: function (orderNumber, callback) {
return this._private('toggleAutoRenew', { orderNumber: orderNumber }, callback);
}
};
// Backwards Compatibility
Poloniex.prototype.getTicker = Poloniex.prototype.returnTicker;
Poloniex.prototype.get24hVolume = Poloniex.prototype.return24hVolume;
Poloniex.prototype.getOrderBook = Poloniex.prototype.returnOrderBook;
Poloniex.prototype.getTradeHistory = Poloniex.prototype.returnChartData;
Poloniex.prototype.myBalances = Poloniex.prototype.returnBalances;
Poloniex.prototype.myOpenOrders = Poloniex.prototype.returnOpenOrders;
Poloniex.prototype.myTradeHistory = Poloniex.prototype.returnTradeHistory;
return Poloniex;
})();
+353
View File
@@ -0,0 +1,353 @@
exports.newAPIClient = function newAPIClient(keyVaultAPI, logger) {
/*
/* ATENTION: This Library is used both a the cloud (AACloud) and also at the AAWeb on the server side without any modifications.
/* There is a second version, stripped of all the inner functionality that runs at AAWeb browser side, which channels the requests to the server side one.
*/
const FULL_LOG = true;
const LOG_FILE_CONTENT = true;
const MODULE_NAME = "poloniexClient";
const retry = require('../exchangeUtils').retry;
const includes = require('../exchangeUtils').includes;
const _ = require('lodash');
const Poloniex = require("./poloniex");
const exchangeProperties = require('./poloniexProperties.json');
const recoverableErrors = [
'SOCKETTIMEDOUT',
'TIMEDOUT',
'CONNRESET',
'CONNREFUSED',
'NOTFOUND',
'429',
'522',
'504',
'503',
'500',
'502',
'Empty response',
'Please try again in a few minutes.',
'Nonce must be greater than',
'Internal error. Please try again.',
'Connection timed out. Please try again.',
'ENOTFOUND',
'ECONNREFUSED',
'CONNREFUSED',
'NOTFOUND',
'ESOCKETTIMEDOUT',
'ECONNRESET',
'Connection timed out',
'Connection Error',
'Order not found, or you are not the person who placed it.',
'ETIMEDOUT'
];
let API = new Poloniex(keyVaultAPI);
let thisObject = {
getTicker: getTicker,
getOpenPositions: getOpenPositions,
getExecutedTrades: getExecutedTrades,
buy: buy,
sell: sell,
movePosition: movePosition,
getPublicTradeHistory: getPublicTradeHistory,
getExchangeProperties: getExchangeProperties,
isValidLot: isValidLot
};
return thisObject;
function joinCurrencies(currencyA, currencyB) {
// If only one arg, then return the first
if (typeof currencyB !== 'string') {
return currencyA;
}
return currencyA + '_' + currencyB;
}
/*
* Returns the properties for this exchange
*/
function getExchangeProperties() {
return exchangeProperties;
}
/*
* Returns the price for a given pair of assets.
* The object returned is an array of trades:
* ticker = {
* bid, Number
* ask, Number
* last Number
* };
*/
function getTicker(pMarket, callBack) {
const handle = (err, response) => {
let ticker;
let market = joinCurrencies(pMarket.assetA, pMarket.assetB);
if (err.result === global.DEFAULT_OK_RESPONSE.result) {
ticker = {
bid: Number(response[market].highestBid),
ask: Number(response[market].lowestAsk),
last: Number(response[market].last)
};
}
callBack(err, ticker);
};
const fetch = next => API.getTicker(analizeResponse(next));
retry(null, fetch, handle);
}
/*
* Returns the open positions ath the exchange for a given market and user account.
* The object returned is an array of positions:
* position = {
* id,
* type,
* rate, Number
* amountA, Number
* amountB, Number
* fee, (not required) Number
* datetime
* };
*/
function getOpenPositions(pMarket, callBack) {
const handle = (err, response) => {
let exchangePositions = [];
if (err.result === global.DEFAULT_OK_RESPONSE.result) {
for (let i = 0; i < response.length; i++) {
let openPosition = {
id: response[i].orderNumber,
type: response[i].type,
rate: Number(response[i].rate),
amountA: Number(response[i].total),
amountB: Number(response[i].amount),
date: (new Date(response[i].time)).valueOf()
};
exchangePositions.push(openPosition);
}
}
callBack(err, exchangePositions);
};
const fetch = next => API.returnOpenOrders(pMarket.assetA, pMarket.assetB, analizeResponse(next));
retry(null, fetch, handle);
}
/*
* Returns the trades for a given order number.
* The object returned is an array of positions:
* position = {
* id,
* type,
* rate, Number
* amountA, Number
* amountB, Number
* fee, Number
* datetime
* };
*/
function getExecutedTrades(pPositionId, callBack) {
const handle = (err, response) => {
let trades = [];
if (err.result === global.DEFAULT_OK_RESPONSE.result) {
for (let i = 0; i < response.length; i++) {
let trade = {
id: response[i].tradeID,
type: response[i].type,
rate: Number(response[i].rate),
amountA: Number(response[i].total),
amountB: Number(response[i].amount),
fee: Number(response[i].fee),
date: (new Date(response[i].date)).valueOf()
}
trades.push(trade);
}
} else if (err.result === global.CUSTOM_OK_RESPONSE.result) {
// No trades found at the exchange for the position, but returning empy trades object.
err = global.DEFAULT_OK_RESPONSE;
}
callBack(err, trades);
};
const fetch = next => API.returnOrderTrades(pPositionId, analizeResponse(next));
retry(null, fetch, handle);
}
/*
* Creates a new buy order.
* The orderNumber is returned. String
*/
function buy(pCurrencyA, pCurrencyB, pRate, pAmount, callBack) {
const handle = (err, response) => {
let orderNumber;
if (err.result === global.DEFAULT_OK_RESPONSE.result) {
orderNumber = response.orderNumber;
}
callBack(err, orderNumber);
};
const fetch = next => API.buy(pCurrencyA, pCurrencyB, pRate, pAmount, analizeResponse(next));
retry(null, fetch, handle);
}
/*
* Creates a new sell order.
* The orderNumber is returned. String
*/
function sell(pCurrencyA, pCurrencyB, pRate, pAmount, callBack) {
const handle = (err, response) => {
let orderNumber;
if (err.result === global.DEFAULT_OK_RESPONSE.result) {
orderNumber = response.orderNumber;
}
callBack(err, orderNumber);
};
const fetch = next => API.sell(pCurrencyA, pCurrencyB, pRate, pAmount, analizeResponse(next));
retry(null, fetch, handle);
}
/*
* Move an existing position to the new rate.
* The orderNumber is returned. String
*/
function movePosition(pPosition, pNewRate, pNewAmountB, callBack) {
const handle = (err, response) => {
let orderNumber;
if (err.result === global.DEFAULT_OK_RESPONSE.result) {
orderNumber = response.orderNumber;
}
callBack(err, orderNumber);
};
const fetch = next => API.moveOrder(pPosition.id, pNewRate, pNewAmountB, analizeResponse(next));
retry(null, fetch, handle);
}
/*
* Returns all the trade history from the Exchange since startTime to endTime orderd by tradeId.
* It's possible that the exchange doesn't support this method.
* The object returned is an array of trades:
* trade = {
* tradeID, String
* globalTradeID, String
* type, String
* rate, Number
* amountA, Number
* amountB, Number
* date Date
* };
*/
function getPublicTradeHistory(assetA, assetB, startTime, endTime, callBack) {
const handle = (err, response) => {
let trades = [];
if (err.result === global.DEFAULT_OK_RESPONSE.result) {
for (let i = 0; i < response.length; i++) {
let trade = {
tradeID: response[i].tradeID,
globalTradeID: response[i].globalTradeID,
type: response[i].type,
rate: Number(response[i].rate),
total: Number(response[i].total),
amount: Number(response[i].amount),
date: new Date(response[i].date)
}
trades.push(trade);
}
}
callBack(err, trades);
};
const fetch = next => API.returnTradeHistory(assetA, assetB, startTime, endTime, analizeResponse(next));
retry(null, fetch, handle);
}
function isValidLot(price, amount) {
return amount * price >= 0.0001;
}
function analizeResponse(callBack) {
return (exchangeErr, exchangeResponse) => {
let error;
/* This function analizes the different situations we might encounter trying to access Poloniex and returns appropiate standard errors. */
let stringExchangeErr = JSON.stringify(exchangeErr);
let stringExchangeResponse = JSON.stringify(exchangeResponse);
try {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] analizeResponse -> exchangeErr = " + stringExchangeErr); }
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] analizeResponse -> exchangeResponse = " + stringExchangeResponse); }
if (exchangeErr) {
error = global.DEFAULT_FAIL_RESPONSE;
if (includes(exchangeErr.code, recoverableErrors)) {
error.notFatal = true;
}
} else if (!exchangeResponse || exchangeResponse === undefined) {
error = {
result: global.DEFAULT_FAIL_RESPONSE.result,
message: 'Empty response',
notFatal: true
};
} else if (includes(exchangeResponse.error, ['Please complete the security check to proceed.'])) {
error = {
result: global.DEFAULT_FAIL_RESPONSE.result,
message: 'The IP has been flagged by CloudFlare.'
};
} else if (includes(exchangeResponse.error, ['<!DOCTYPE html>'])) {
error = {
result: global.DEFAULT_FAIL_RESPONSE.result,
message: exchangeResponse
};
} else if (includes(exchangeResponse.error, ['Not enough'])) {
error = {
result: global.DEFAULT_RETRY_RESPONSE.result,
message: exchangeResponse.error
};
} else if (includes(exchangeResponse.error, ['Order not found, or you are not the person who placed it.'])) {
error = {
result: global.CUSTOM_OK_RESPONSE.result,
message: exchangeResponse.error
};
} else if (exchangeResponse.error) {
error = {
result: global.DEFAULT_FAIL_RESPONSE.result,
message: exchangeResponse.error
};
}
return callBack(error, exchangeResponse);
} catch (err) {
logger.write(MODULE_NAME, "[ERROR] analizeResponse -> err.message = " + err.message);
return callBack(global.DEFAULT_FAIL_RESPONSE, exchangeResponse);
}
}
}
}
File diff suppressed because it is too large. Load diff