Merge branch 'master' of https://github.com/AdvancedAlgos/AACloud
This commit is contained in:
commit
a00713cbe7
20 files changed
+10362
-962
No files matched your search
+60
-42
@@ -89,7 +89,7 @@
|
||||
|
||||
if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] initialize -> getMarketRateFromExchange -> Entering function."); }
|
||||
|
||||
exchangeAPI.getTicker(global.MARKET.assetA + '_' + global.MARKET.assetB, onTicker);
|
||||
exchangeAPI.getTicker(global.MARKET, onTicker);
|
||||
|
||||
return;
|
||||
|
||||
@@ -412,15 +412,15 @@
|
||||
|
||||
/*
|
||||
|
||||
Here we check that all the positions we know we still have at the exchange. If they are not, we will try to take appropiate
|
||||
Here we check that all the positions we know are still valid at the exchange. If they are not, we will try to take appropiate
|
||||
actions. Reasons why the positions might not be there are:
|
||||
|
||||
1. The user / account owner closed the positions manually.
|
||||
2. The exchange for some eventuality closed the positions. In some exchanges positions have an expiratin time.
|
||||
2. The exchange for some eventuality closed the positions. In some exchanges positions have an expiration time.
|
||||
3. The orders were executed.
|
||||
|
||||
For as 1 and 2 are similar unexpected situations and we will stop the bot execution when we detect we are there. Number 3 is an
|
||||
expected behaviour and we take appropiate action.
|
||||
Situations 1 and 2 are similar unexpected and we will stop the bot execution when we detect them. Number 3 is an
|
||||
expected behaviour and we will take appropiate action.
|
||||
|
||||
Also, the position might still be there but it might have been partially executed. To detect that we need to compare the
|
||||
position amounts we have on file to the one we are receiving from the exchange.
|
||||
@@ -445,7 +445,7 @@
|
||||
/*
|
||||
|
||||
This removes all orders leaving only the not executed position. Consider that we do want executed positions to be recored on the file so as to
|
||||
have a record of their execution. But at next execution these records must be deleted bedore the new Execution Context file is created and
|
||||
have a record of their execution. But at next execution these records must be deleted before the new Execution Context file is created and
|
||||
this is what we do here.
|
||||
|
||||
*/
|
||||
@@ -488,14 +488,18 @@
|
||||
if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] ordersExecutionCheck -> loopBody -> positionFound -> Entering function."); }
|
||||
|
||||
/*
|
||||
|
||||
We update the final amountA based on what we got from the exchange.
|
||||
This is needed since the values could be different from what we calculate.
|
||||
*/
|
||||
position.amountA = exchangePosition.amountA;
|
||||
|
||||
/*
|
||||
We need to know if the order was partially executed. To know that, we compare the amounts on file with the ones
|
||||
received from the exchange. If they are the same, then the order is intact. Otherwise, to confirm a partial execution,
|
||||
we will request the associated trades from the exchange.
|
||||
|
||||
*/
|
||||
|
||||
if (position.amountB === thisObject.truncDecimals(exchangePosition.amountB)) {
|
||||
if (position.amountB === exchangePosition.amountB) {
|
||||
|
||||
/* Position is still there, untouched. Nothing to do here. */
|
||||
|
||||
@@ -557,10 +561,7 @@
|
||||
|
||||
if (thisPosition.id === pPositionId) {
|
||||
|
||||
let feeRate = 0.002; // Default fee
|
||||
|
||||
if (Math.random(1) < 0.5)
|
||||
feeRate = 0.0015; // Some times we will pay less fee
|
||||
let feeRate = 0.002; // Default backtesting fee simulation
|
||||
|
||||
let trade = {
|
||||
id: Math.trunc(Math.random(1) * 1000000),
|
||||
@@ -663,10 +664,10 @@
|
||||
sumAssetB = thisObject.truncDecimals(sumAssetB);
|
||||
|
||||
if (position.amountB !== sumAssetB) {
|
||||
logger.write(MODULE_NAME, "[ERROR] ordersExecutionCheck -> loopBody -> position.amountB = " + position.amountB);
|
||||
logger.write(MODULE_NAME, "[ERROR] ordersExecutionCheck -> loopBody -> sumAssetB = " + sumAssetB);
|
||||
logger.write(MODULE_NAME, "[INFO] ordersExecutionCheck -> loopBody -> position.amountB = " + position.amountB);
|
||||
logger.write(MODULE_NAME, "[INFO] ordersExecutionCheck -> loopBody -> sumAssetB = " + sumAssetB);
|
||||
|
||||
logger.write(MODULE_NAME, "[ERROR] ordersExecutionCheck -> loopBody -> confirmOrderWasExecuted -> Cannot be confirmed that the order was executed. It must be manually cancelled by the user or cancelled by the exchange itself.");
|
||||
logger.write(MODULE_NAME, "[INFO] ordersExecutionCheck -> loopBody -> confirmOrderWasExecuted -> Cannot be confirmed that the order was executed. It must be manually cancelled by the user or cancelled by the exchange itself.");
|
||||
logger.write(MODULE_NAME, "[HINT] ordersExecutionCheck -> loopBody -> confirmOrderWasExecuted -> If the process was abruptally cancelled and then restarted, it is possible that now is not sincronized with the exchange.");
|
||||
logger.write(MODULE_NAME, "[HINT] ordersExecutionCheck -> loopBody -> confirmOrderWasExecuted -> In any case, to continue, you must manually delete the orders at the exchange.");
|
||||
callBack(global.DEFAULT_FAIL_RESPONSE);
|
||||
@@ -715,8 +716,8 @@
|
||||
/*
|
||||
|
||||
To confirm everything is ok, we will add all the amounts on trades plus the remaining amounts
|
||||
at the order at the exchange and they must be equal to the one on file. Otherwise something very strange could
|
||||
have happened, in which case we will halt the bot execution.
|
||||
at the order at the exchange and they must be equal to the one on file. Otherwise an inconsistent
|
||||
state has been detected, in which case we will halt the bot execution.
|
||||
|
||||
*/
|
||||
|
||||
@@ -737,18 +738,28 @@
|
||||
sumAssetA = thisObject.truncDecimals(sumAssetA);
|
||||
sumAssetB = thisObject.truncDecimals(sumAssetB);
|
||||
|
||||
const EXCHANGE_PRECISION = 0.00001; // This value was determined empirically. We noticed that the sume of the trades plus the remaining order at the exchange didn't exatly matched the positions we keep at the execution context, and the only explanation found was that the exchange presicion was not big enough to make it match. So to avoid unnecesary warnings, we created this constant that filters out this situation.
|
||||
/*
|
||||
* Next let's get the max decimal positions for the pair being trade returned by the exchange and validate that at least
|
||||
* one decimal position less than the max matches. The reason for this is because there could be some
|
||||
* rounding on the exchange when there are partial executed orders that will interfiere with the exact validation.
|
||||
*
|
||||
* IE: For Poloniex USDT/BTC the max returned decimal positions is 8, it means we will validate that the diference is less than 0.0000001
|
||||
*/
|
||||
|
||||
if (Math.abs(thisObject.truncDecimals(position.amountA) - sumAssetA) > EXCHANGE_PRECISION || Math.abs(position.amountB - sumAssetB) > EXCHANGE_PRECISION) {
|
||||
let exchangeParam = exchangeAPI.getMaxDecimalPositions();
|
||||
let minValue = '0.' + (1).toPrecision(exchangeParam-1).split('.').reverse().join('');
|
||||
let exchangePrecision = parseFloat(parseFloat(minValue).toFixed(exchangeParam));
|
||||
|
||||
logger.write(MODULE_NAME, "[ERROR] ordersExecutionCheck -> loopBody -> confirmOrderWasPartiallyExecuted -> position.amountA = " + position.amountA);
|
||||
logger.write(MODULE_NAME, "[ERROR] ordersExecutionCheck -> loopBody -> confirmOrderWasPartiallyExecuted -> sumAssetA = " + sumAssetA);
|
||||
logger.write(MODULE_NAME, "[ERROR] ordersExecutionCheck -> loopBody -> confirmOrderWasPartiallyExecuted -> position.amountB = " + position.amountB);
|
||||
logger.write(MODULE_NAME, "[ERROR] ordersExecutionCheck -> loopBody -> confirmOrderWasPartiallyExecuted -> sumAssetB = " + sumAssetB);
|
||||
if (Math.abs(position.amountA - sumAssetA) > exchangePrecision || Math.abs(position.amountB - sumAssetB) > exchangePrecision) {
|
||||
|
||||
logger.write(MODULE_NAME, "[ERROR] ordersExecutionCheck -> loopBody -> confirmOrderWasPartiallyExecuted -> Cannot be confirmed that a partiall execution was done well.");
|
||||
logger.write(MODULE_NAME, "[INFO] ordersExecutionCheck -> loopBody -> confirmOrderWasPartiallyExecuted -> position.amountA = " + position.amountA);
|
||||
logger.write(MODULE_NAME, "[INFO] ordersExecutionCheck -> loopBody -> confirmOrderWasPartiallyExecuted -> sumAssetA = " + sumAssetA);
|
||||
logger.write(MODULE_NAME, "[INFO] ordersExecutionCheck -> loopBody -> confirmOrderWasPartiallyExecuted -> position.amountB = " + position.amountB);
|
||||
logger.write(MODULE_NAME, "[INFO] ordersExecutionCheck -> loopBody -> confirmOrderWasPartiallyExecuted -> sumAssetB = " + sumAssetB);
|
||||
|
||||
logger.write(MODULE_NAME, "[ERROR] ordersExecutionCheck -> loopBody -> confirmOrderWasPartiallyExecuted -> Cannot confirm that a partial execution was done well.");
|
||||
|
||||
/* There are diferences on the responses between the getPosition and getTrades that causes some issues, let's retry. */
|
||||
/* There are diferences on the responses between the getPosition and getTrades that causes some issues, let's retry. */
|
||||
callBack(global.DEFAULT_RETRY_RESPONSE);
|
||||
return;
|
||||
}
|
||||
@@ -815,13 +826,13 @@
|
||||
|
||||
let feeAmount = thisObject.truncDecimals(Number(trade.fee) * Number(trade.amountB));
|
||||
|
||||
assetA = thisObject.truncDecimals(trade.amountA);
|
||||
assetB = thisObject.truncDecimals(trade.amountB) - feeAmount;
|
||||
assetA = Number(trade.amountA);
|
||||
assetB = Number(trade.amountB) - feeAmount;
|
||||
|
||||
context.executionContext.balance.assetA = thisObject.truncDecimals(context.executionContext.balance.assetA) - assetA;
|
||||
context.executionContext.balance.assetB = thisObject.truncDecimals(context.executionContext.balance.assetB) + assetB;
|
||||
context.executionContext.balance.assetA = thisObject.truncDecimals(context.executionContext.balance.assetA - assetA);
|
||||
context.executionContext.balance.assetB = thisObject.truncDecimals(context.executionContext.balance.assetB + assetB);
|
||||
|
||||
context.executionContext.availableBalance.assetB = thisObject.truncDecimals(context.executionContext.availableBalance.assetB) + assetB;
|
||||
context.executionContext.availableBalance.assetB = thisObject.truncDecimals(context.executionContext.availableBalance.assetB + assetB);
|
||||
|
||||
/* Not the available balance for asset A is not affected since it was already reduced when the order was placed. */
|
||||
|
||||
@@ -838,13 +849,13 @@
|
||||
|
||||
let feeAmount = thisObject.truncDecimals(Number(trade.fee) * Number(trade.amountA));
|
||||
|
||||
assetA = thisObject.truncDecimals(trade.amountA) - feeAmount;
|
||||
assetB = thisObject.truncDecimals(trade.amountB);
|
||||
assetA = Number(trade.amountA) - feeAmount;
|
||||
assetB = Number(trade.amountB);
|
||||
|
||||
context.executionContext.balance.assetA = thisObject.truncDecimals(context.executionContext.balance.assetA) + assetA;
|
||||
context.executionContext.balance.assetB = thisObject.truncDecimals(context.executionContext.balance.assetB) - assetB;
|
||||
context.executionContext.balance.assetA = thisObject.truncDecimals(context.executionContext.balance.assetA + assetA);
|
||||
context.executionContext.balance.assetB = thisObject.truncDecimals(context.executionContext.balance.assetB - assetB);
|
||||
|
||||
context.executionContext.availableBalance.assetA = thisObject.truncDecimals(context.executionContext.availableBalance.assetA) + assetA;
|
||||
context.executionContext.availableBalance.assetA = thisObject.truncDecimals(context.executionContext.availableBalance.assetA + assetA);
|
||||
|
||||
/* Not the available balance for asset B is not affected since it was already reduced when the order was placed. */
|
||||
|
||||
@@ -1069,15 +1080,13 @@
|
||||
|
||||
if (position.type === 'buy') {
|
||||
|
||||
context.executionContext.availableBalance.assetA = thisObject.truncDecimals(context.executionContext.availableBalance.assetA) - pAmountA;
|
||||
context.executionContext.availableBalance.assetA = thisObject.truncDecimals(context.executionContext.availableBalance.assetA);
|
||||
context.executionContext.availableBalance.assetA = thisObject.truncDecimals(context.executionContext.availableBalance.assetA - pAmountA);
|
||||
context.newHistoryRecord.lastBuyRate = pRate;
|
||||
}
|
||||
|
||||
if (position.type === 'sell') {
|
||||
|
||||
context.executionContext.availableBalance.assetB = thisObject.truncDecimals(context.executionContext.availableBalance.assetB) - pAmountB;
|
||||
context.executionContext.availableBalance.assetB = thisObject.truncDecimals(context.executionContext.availableBalance.assetB);
|
||||
context.executionContext.availableBalance.assetB = thisObject.truncDecimals(context.executionContext.availableBalance.assetB - pAmountB);
|
||||
context.newHistoryRecord.lastSellRate = pRate;
|
||||
}
|
||||
|
||||
@@ -1233,6 +1242,13 @@
|
||||
}
|
||||
}
|
||||
|
||||
function getPublicTradeHistory(assetA, assetB, startTime, endTime, callback) {
|
||||
|
||||
if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] getTradeHistory -> Entering function."); }
|
||||
|
||||
exchangeAPI.getPublicTradeHistory(assetA, assetB, startTime, endTime, callback);
|
||||
}
|
||||
|
||||
function recalculateRateAverages() {
|
||||
|
||||
/* This function calculates the weighted rates of buy and sell positions. This is a way to summarize with only one rate, where the positions are. */
|
||||
@@ -1409,9 +1425,11 @@
|
||||
}
|
||||
}
|
||||
|
||||
function truncDecimals(pFloatValue) {
|
||||
function truncDecimals(pFloatValue, pDecimals) {
|
||||
|
||||
return parseFloat(parseFloat(pFloatValue).toFixed(8));
|
||||
if (!pDecimals) pDecimals = 8; // Default value
|
||||
|
||||
return parseFloat(parseFloat(pFloatValue).toFixed(pDecimals));
|
||||
|
||||
}
|
||||
};
|
||||
+9
-1
@@ -330,7 +330,7 @@
|
||||
*/
|
||||
|
||||
const INITIAL_INVESTMENT_A = 0; // This is just for this release of the platform.
|
||||
const INITIAL_INVESTMENT_B = .002 / totalAlgobots; // This is just for this release of the platform.
|
||||
const INITIAL_INVESTMENT_B = truncDecimals(.002 / totalAlgobots); // This is just for this release of the platform.
|
||||
|
||||
thisObject.executionContext = {
|
||||
investment: { // This is used to calculate profits.
|
||||
@@ -562,4 +562,12 @@
|
||||
}
|
||||
}
|
||||
|
||||
function truncDecimals(pFloatValue, pDecimals) {
|
||||
|
||||
if (!pDecimals) pDecimals = 8; // Default value
|
||||
|
||||
return parseFloat(parseFloat(pFloatValue).toFixed(pDecimals));
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
-378
@@ -1,378 +0,0 @@
|
||||
exports.newExchangeAPI = function newExchangeAPI(BOT, logger, POLONIEX_CLIENT_MODULE) {
|
||||
|
||||
/*
|
||||
|
||||
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 MODULE_NAME = "Exchange API";
|
||||
|
||||
let bot = BOT;
|
||||
|
||||
let thisObject = {
|
||||
initialize: initialize,
|
||||
getOpenPositions: getOpenPositions,
|
||||
getExecutedTrades: getExecutedTrades,
|
||||
putPosition: putPosition,
|
||||
movePosition: movePosition,
|
||||
getTicker: getTicker
|
||||
};
|
||||
|
||||
let poloniexApiClient;
|
||||
|
||||
return thisObject;
|
||||
|
||||
function initialize(callBackFunction) {
|
||||
|
||||
try {
|
||||
|
||||
if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] initialize -> Entering function."); }
|
||||
|
||||
poloniexApiClient = POLONIEX_CLIENT_MODULE.newPoloniexAPIClient(global.EXCHANGE_KEYS[global.EXCHANGE_NAME][bot.instanceIndex].Key, global.EXCHANGE_KEYS[global.EXCHANGE_NAME][bot.instanceIndex].Secret);
|
||||
|
||||
callBackFunction(global.DEFAULT_OK_RESPONSE);
|
||||
|
||||
} catch (err) {
|
||||
|
||||
logger.write(MODULE_NAME, "[ERROR] initialize -> err = " + err.message);
|
||||
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
|
||||
}
|
||||
}
|
||||
|
||||
function getOpenPositions(pMarket, callBackFunction) {
|
||||
|
||||
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)); }
|
||||
|
||||
poloniexApiClient.API.returnOpenOrders(pMarket.assetA, pMarket.assetB, onExchangeCallReturned);
|
||||
|
||||
function onExchangeCallReturned(err, exchangeResponse) {
|
||||
|
||||
try {
|
||||
|
||||
if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] getOpenPositions -> onExchangeCallReturned -> Entering function."); }
|
||||
if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] getOpenPositions -> onExchangeCallReturned -> err = " + err); }
|
||||
if (global.LOG_CONTROL[MODULE_NAME].logContent === true) { logger.write(MODULE_NAME, "[INFO] getOpenPositions -> onExchangeCallReturned -> exchangeResponse = " + JSON.stringify(exchangeResponse)); }
|
||||
|
||||
poloniexApiClient.API.analizeResponse(logger, err, exchangeResponse, callBackFunction, onResponseOk);
|
||||
|
||||
function onResponseOk() {
|
||||
|
||||
/*
|
||||
|
||||
This is what we receive from the exchange. We will convert this to our standard format for later use.
|
||||
|
||||
[ { orderNumber: '151918418632',
|
||||
type: 'sell',
|
||||
rate: '20000.00000000',
|
||||
startingAmount: '0.00010000',
|
||||
amount: '0.00010000',
|
||||
total: '2.00000000',
|
||||
date: '2018-02-24 11:14:17',
|
||||
margin: 0 } ]
|
||||
|
||||
*/
|
||||
|
||||
let exchangePositions = [];
|
||||
|
||||
for (let i = 0; i < exchangeResponse.length; i++) {
|
||||
|
||||
let openPosition = {
|
||||
id: exchangeResponse[i].orderNumber,
|
||||
type: exchangeResponse[i].type,
|
||||
rate: exchangeResponse[i].rate,
|
||||
amountA: exchangeResponse[i].total,
|
||||
amountB: exchangeResponse[i].amount,
|
||||
date: (new Date(exchangeResponse[i].date)).valueOf()
|
||||
};
|
||||
|
||||
exchangePositions.push(openPosition);
|
||||
}
|
||||
|
||||
callBackFunction(global.DEFAULT_OK_RESPONSE, exchangePositions);
|
||||
}
|
||||
}
|
||||
catch (err) {
|
||||
logger.write(MODULE_NAME, "[ERROR] getOpenPositions -> onExchangeCallReturned -> Error = " + err.message);
|
||||
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
logger.write(MODULE_NAME, "[ERROR] getOpenPositions -> Error = " + err.message);
|
||||
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
|
||||
}
|
||||
}
|
||||
|
||||
function getExecutedTrades(pPositionId, callBackFunction) {
|
||||
|
||||
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); }
|
||||
|
||||
poloniexApiClient.API.returnOrderTrades(pPositionId, onExchangeCallReturned);
|
||||
|
||||
function onExchangeCallReturned(err, exchangeResponse) {
|
||||
|
||||
try {
|
||||
|
||||
if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] getExecutedTrades -> onExchangeCallReturned -> Entering function."); }
|
||||
if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] getExecutedTrades -> onExchangeCallReturned -> err = " + err); }
|
||||
if (global.LOG_CONTROL[MODULE_NAME].logContent === true) { logger.write(MODULE_NAME, "[INFO] getExecutedTrades -> onExchangeCallReturned -> exchangeResponse = " + JSON.stringify(exchangeResponse)); }
|
||||
|
||||
poloniexApiClient.API.analizeResponse(logger, err, exchangeResponse, callBackFunction, onResponseOk);
|
||||
|
||||
function onResponseOk() {
|
||||
|
||||
/*
|
||||
|
||||
This is what we receive from the exchange. We will convert this to our standard format for later use.
|
||||
|
||||
[
|
||||
{
|
||||
"globalTradeID": 20825863,
|
||||
"tradeID": 147142,
|
||||
"currencyPair":
|
||||
"BTC_XVC",
|
||||
"type": "buy",
|
||||
"rate": "0.00018500",
|
||||
"amount": "455.34206390",
|
||||
"total": "0.08423828",
|
||||
"fee": "0.00200000",
|
||||
"date": "2016-03-14 01:04:36"
|
||||
},
|
||||
...]
|
||||
|
||||
*/
|
||||
|
||||
let trades = [];
|
||||
|
||||
for (let i = 0; i < exchangeResponse.length; i++) {
|
||||
|
||||
let trade = {
|
||||
id: exchangeResponse[i].tradeID,
|
||||
type: exchangeResponse[i].type,
|
||||
rate: exchangeResponse[i].rate,
|
||||
amountA: exchangeResponse[i].total,
|
||||
amountB: exchangeResponse[i].amount,
|
||||
fee: exchangeResponse[i].fee,
|
||||
date: (new Date(exchangeResponse[i].date)).valueOf()
|
||||
}
|
||||
|
||||
trades.push(trade);
|
||||
}
|
||||
|
||||
if (global.LOG_CONTROL[MODULE_NAME].logContent === true) { logger.write(MODULE_NAME, "[INFO] getExecutedTrades -> onExchangeCallReturned -> trades = " + JSON.stringify(trades)); }
|
||||
|
||||
callBackFunction(global.DEFAULT_OK_RESPONSE, trades);
|
||||
}
|
||||
}
|
||||
catch (err) {
|
||||
logger.write(MODULE_NAME, "[ERROR] getExecutedTrades -> onExchangeCallReturned -> Error = " + err.message);
|
||||
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
logger.write(MODULE_NAME, "[ERROR] getExecutedTrades -> Error = " + err.message);
|
||||
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
|
||||
}
|
||||
}
|
||||
|
||||
function putPosition(pMarket, pType, pRate, pAmountA, pAmountB, callBackFunction) {
|
||||
|
||||
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 = " + pRate); }
|
||||
if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] putPosition -> pAmountA = " + pAmountA); }
|
||||
if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] putPosition -> pAmountB = " + pAmountB); }
|
||||
|
||||
if (pType === "buy") {
|
||||
poloniexApiClient.API.buy(pMarket.assetA, pMarket.assetB, pRate, pAmountB, onExchangeCallReturned);
|
||||
return;
|
||||
}
|
||||
|
||||
if (pType === "sell") {
|
||||
poloniexApiClient.API.sell(pMarket.assetA, pMarket.assetB, pRate, pAmountB, onExchangeCallReturned);
|
||||
return;
|
||||
}
|
||||
|
||||
logger.write(MODULE_NAME, "[ERROR] putPosition -> pType must be either 'buy' or 'sell'.");
|
||||
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
|
||||
return;
|
||||
|
||||
function onExchangeCallReturned(err, exchangeResponse) {
|
||||
|
||||
try {
|
||||
|
||||
if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] putPosition -> onExchangeCallReturned -> Entering function."); }
|
||||
if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] putPosition -> onExchangeCallReturned -> err = " + err); }
|
||||
if (global.LOG_CONTROL[MODULE_NAME].logContent === true) { logger.write(MODULE_NAME, "[INFO] putPosition -> onExchangeCallReturned -> exchangeResponse = " + JSON.stringify(exchangeResponse)); }
|
||||
|
||||
poloniexApiClient.API.analizeResponse(logger, err, exchangeResponse, callBackFunction, onResponseOk);
|
||||
|
||||
function onResponseOk() {
|
||||
|
||||
/*
|
||||
|
||||
This is what we can receive from the exchange.
|
||||
|
||||
{
|
||||
"orderNumber":31226040,
|
||||
"resultingTrades":
|
||||
[{
|
||||
"amount":"338.8732",
|
||||
"date":"2014-10-18 23:03:21",
|
||||
"rate":"0.00000173",
|
||||
"total":"0.00058625",
|
||||
"tradeID":"16164",
|
||||
"type":"buy"
|
||||
}]
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
callBackFunction(global.DEFAULT_OK_RESPONSE, exchangeResponse.orderNumber);
|
||||
}
|
||||
}
|
||||
catch (err) {
|
||||
logger.write(MODULE_NAME, "[ERROR] putPosition -> onExchangeCallReturned -> Error = " + err.message);
|
||||
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
|
||||
}
|
||||
}
|
||||
|
||||
} catch (err) {
|
||||
logger.write(MODULE_NAME, "[ERROR] putPosition -> err = " + err.message);
|
||||
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
|
||||
}
|
||||
}
|
||||
|
||||
function movePosition(pPosition, pNewRate, pNewAmountB, callBackFunction) {
|
||||
|
||||
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 = " + pNewRate); }
|
||||
|
||||
poloniexApiClient.API.moveOrder(pPosition.id, pNewRate, pNewAmountB, onExchangeCallReturned);
|
||||
|
||||
function onExchangeCallReturned(err, exchangeResponse) {
|
||||
|
||||
if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] movePosition -> onExchangeCallReturned -> Entering function."); }
|
||||
if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] movePosition -> onExchangeCallReturned -> err = " + err); }
|
||||
if (global.LOG_CONTROL[MODULE_NAME].logContent === true) { logger.write(MODULE_NAME, "[INFO] movePosition -> onExchangeCallReturned -> exchangeResponse = " + JSON.stringify(exchangeResponse)); }
|
||||
|
||||
try {
|
||||
|
||||
poloniexApiClient.API.analizeResponse(logger, err, exchangeResponse, callBackFunction, onResponseOk);
|
||||
|
||||
function onResponseOk() {
|
||||
|
||||
if (exchangeResponse.success !== 1) {
|
||||
logger.write(MODULE_NAME, "[ERROR] movePosition -> onExchangeCallReturned -> exchangeResponse.success = " + exchangeResponse.success);
|
||||
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
|
||||
return;
|
||||
}
|
||||
/*
|
||||
|
||||
This is what we can receive from the exchange.
|
||||
|
||||
{
|
||||
"orderNumber":31226040,
|
||||
"resultingTrades":
|
||||
[{
|
||||
"amount":"338.8732",
|
||||
"date":"2014-10-18 23:03:21",
|
||||
"rate":"0.00000173",
|
||||
"total":"0.00058625",
|
||||
"tradeID":"16164",
|
||||
"type":"buy"
|
||||
}]
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
callBackFunction(global.DEFAULT_OK_RESPONSE, exchangeResponse.orderNumber);
|
||||
}
|
||||
}
|
||||
catch (err) {
|
||||
logger.write(MODULE_NAME, "[ERROR] movePosition -> onExchangeCallReturned -> Error = " + err.message);
|
||||
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
|
||||
}
|
||||
}
|
||||
|
||||
} catch (err) {
|
||||
logger.write(MODULE_NAME, "[ERROR] movePosition -> err = " + err.message);
|
||||
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
|
||||
}
|
||||
}
|
||||
|
||||
function getTicker(pMarket, callBackFunction) {
|
||||
|
||||
try {
|
||||
|
||||
if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] getTicker -> Entering function."); }
|
||||
|
||||
poloniexApiClient.API.returnTicker(onExchangeCallReturned);
|
||||
|
||||
function onExchangeCallReturned(err, exchangeResponse) {
|
||||
|
||||
if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] movePosition -> onExchangeCallReturned -> Entering function."); }
|
||||
if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] movePosition -> onExchangeCallReturned -> err = " + err); }
|
||||
if (global.LOG_CONTROL[MODULE_NAME].logContent === true) { logger.write(MODULE_NAME, "[INFO] movePosition -> onExchangeCallReturned -> exchangeResponse = " + JSON.stringify(exchangeResponse)); }
|
||||
|
||||
try {
|
||||
|
||||
poloniexApiClient.API.analizeResponse(logger, err, exchangeResponse, callBackFunction, onResponseOk);
|
||||
|
||||
function onResponseOk() {
|
||||
|
||||
/*
|
||||
|
||||
This is what we can receive from the exchange.
|
||||
|
||||
{"BTC_LTC":{ "last":"0.0251",
|
||||
"lowestAsk":"0.02589999",
|
||||
"highestBid":"0.0251",
|
||||
"percentChange":"0.02390438",
|
||||
"baseVolume":"6.16485315",
|
||||
"quoteVolume":"245.82513926"
|
||||
},
|
||||
"BTC_NXT":{ "last":"0.00005730",
|
||||
"lowestAsk":"0.00005710",
|
||||
"highestBid":"0.00004903",
|
||||
"percentChange":"0.16701570",
|
||||
"baseVolume":"0.45347489",
|
||||
"quoteVolume":"9094"
|
||||
},
|
||||
... }
|
||||
|
||||
*/
|
||||
|
||||
let ticker = {
|
||||
bid: Number(exchangeResponse[pMarket].highestBid),
|
||||
ask: Number(exchangeResponse[pMarket].lowestAsk),
|
||||
last: Number(exchangeResponse[pMarket].last)
|
||||
};
|
||||
|
||||
callBackFunction(global.DEFAULT_OK_RESPONSE, ticker);
|
||||
}
|
||||
}
|
||||
catch (err) {
|
||||
logger.write(MODULE_NAME, "[ERROR] movePosition -> onExchangeCallReturned -> Error = " + err.message);
|
||||
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
|
||||
}
|
||||
}
|
||||
|
||||
} catch (err) {
|
||||
logger.write(MODULE_NAME, "[ERROR] movePosition -> err = " + err.message);
|
||||
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -171,7 +171,7 @@
|
||||
|
||||
const UTILITIES = require(ROOT_DIR + 'CloudUtilities');
|
||||
const STATUS_REPORT = require(ROOT_DIR + 'StatusReport');
|
||||
const POLONIEX_CLIENT_MODULE = require(ROOT_DIR + 'PoloniexAPIClient');
|
||||
const EXCHANGE_API = require(ROOT_DIR + 'exchange/ExchangeAPI');
|
||||
const STATUS_DEPENDENCIES = require(ROOT_DIR + 'StatusDependencies');
|
||||
|
||||
/* We define the datetime for the process that we are running now. This will be the official processing time for both the infraestructure and the bot. */
|
||||
@@ -182,6 +182,7 @@
|
||||
|
||||
let userBot;
|
||||
let statusDependencies;
|
||||
let exchangeAPI;
|
||||
|
||||
let nextWaitTime;
|
||||
|
||||
@@ -206,7 +207,7 @@
|
||||
switch (err.result) {
|
||||
case global.DEFAULT_OK_RESPONSE.result: {
|
||||
logger.write(MODULE_NAME, "[INFO] run -> loop -> initializeStatusDependencies -> onInizialized -> Execution finished well.");
|
||||
initializeUserBot();
|
||||
initializeExchangeAPI();
|
||||
return;
|
||||
}
|
||||
case global.DEFAULT_RETRY_RESPONSE.result: { // Something bad happened, but if we retry in a while it might go through the next time.
|
||||
@@ -261,13 +262,87 @@
|
||||
}
|
||||
}
|
||||
|
||||
function initializeExchangeAPI() {
|
||||
|
||||
try {
|
||||
|
||||
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> initializeExchangeAPI -> Entering function."); }
|
||||
|
||||
exchangeAPI = EXCHANGE_API.newExchangeAPI(bot, logger);
|
||||
|
||||
exchangeAPI.initialize(onInizialized);
|
||||
|
||||
function onInizialized(err) {
|
||||
|
||||
try {
|
||||
|
||||
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> initializeContext -> onInizialized -> onInizialized -> Entering function."); }
|
||||
|
||||
switch (err.result) {
|
||||
case global.DEFAULT_OK_RESPONSE.result: {
|
||||
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> initializeExchangeAPI -> onInizialized -> Execution finished well."); }
|
||||
initializeUserBot();
|
||||
return;
|
||||
}
|
||||
case global.DEFAULT_RETRY_RESPONSE.result: { // Something bad happened, but if we retry in a while it might go through the next time.
|
||||
logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeExchangeAPI -> onInizialized -> Retry Later. Requesting Execution Retry.");
|
||||
nextWaitTime = 'Retry';
|
||||
loopControl(nextWaitTime);
|
||||
return;
|
||||
}
|
||||
case global.DEFAULT_FAIL_RESPONSE.result: { // This is an unexpected exception that we do not know how to handle.
|
||||
logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeExchangeAPI -> onInizialized -> Operation Failed. Aborting the process.");
|
||||
logger.persist();
|
||||
clearInterval(fixedTimeLoopIntervalHandle);
|
||||
clearTimeout(nextLoopTimeoutHandle);
|
||||
clearTimeout(checkLoopHealthHandle);
|
||||
bot.enableCheckLoopHealth = false;
|
||||
callBackFunction(err);
|
||||
return;
|
||||
}
|
||||
default: {
|
||||
logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeExchangeAPI -> onInizialized -> Unhandled err.result received. -> err.result = " + err.result);
|
||||
logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeExchangeAPI -> onInizialized -> Unhandled err.result received. -> err.message = " + err.message);
|
||||
|
||||
logger.persist();
|
||||
clearInterval(fixedTimeLoopIntervalHandle);
|
||||
clearTimeout(nextLoopTimeoutHandle);
|
||||
clearTimeout(checkLoopHealthHandle);
|
||||
bot.enableCheckLoopHealth = false;
|
||||
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
} catch (err) {
|
||||
logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeContext -> onInizialized -> onInizialized -> err = " + err.message);
|
||||
logger.persist();
|
||||
clearInterval(fixedTimeLoopIntervalHandle);
|
||||
clearTimeout(nextLoopTimeoutHandle);
|
||||
clearTimeout(checkLoopHealthHandle);
|
||||
bot.enableCheckLoopHealth = false;
|
||||
callBackFunction(err);
|
||||
}
|
||||
}
|
||||
|
||||
} catch (err) {
|
||||
logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeExchangeAPI -> err = " + err.message);
|
||||
logger.persist();
|
||||
clearInterval(fixedTimeLoopIntervalHandle);
|
||||
clearTimeout(nextLoopTimeoutHandle);
|
||||
clearTimeout(checkLoopHealthHandle);
|
||||
bot.enableCheckLoopHealth = false;
|
||||
callBackFunction(err);
|
||||
}
|
||||
}
|
||||
|
||||
function initializeUserBot() {
|
||||
|
||||
try {
|
||||
|
||||
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> initializeUserBot -> Entering function."); }
|
||||
|
||||
usertBot = USER_BOT_MODULE.newUserBot(bot, logger, COMMONS_MODULE, UTILITIES, BLOB_STORAGE, STATUS_REPORT, POLONIEX_CLIENT_MODULE);
|
||||
usertBot = USER_BOT_MODULE.newUserBot(bot, logger, COMMONS_MODULE, UTILITIES, BLOB_STORAGE, STATUS_REPORT, exchangeAPI);
|
||||
|
||||
usertBot.initialize(statusDependencies, pMonth, pYear, onInizialized);
|
||||
|
||||
|
||||
@@ -1,501 +0,0 @@
|
||||
exports.newPoloniexAPIClient = function newPoloniexAPIClient(pKey, pSecret) {
|
||||
|
||||
/*
|
||||
/* 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.
|
||||
*/
|
||||
|
||||
let newFunction = new API(pKey, pSecret);
|
||||
|
||||
let thisObject = {
|
||||
API: newFunction
|
||||
};
|
||||
|
||||
return thisObject;
|
||||
|
||||
function API(key, secret) {
|
||||
'use strict';
|
||||
|
||||
// Module dependencies
|
||||
var crypto = require('crypto'),
|
||||
request = require('request'),
|
||||
nonce = require('nonce')();
|
||||
|
||||
// Constants
|
||||
var version = '0.0.8',
|
||||
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;
|
||||
}
|
||||
|
||||
// Generate headers signed by thisObject user's key and secret.
|
||||
// The secret is encapsulated and never exposed
|
||||
function _getPrivateHeaders(parameters) {
|
||||
var paramString, signature;
|
||||
|
||||
if (!key || !secret) {
|
||||
throw 'Poloniex: Error. API key and secret required';
|
||||
}
|
||||
|
||||
// Convert to `arg1=foo&arg2=bar`
|
||||
paramString = Object.keys(parameters).map(function (param) {
|
||||
return encodeURIComponent(param) + '=' + encodeURIComponent(parameters[param]);
|
||||
}).join('&');
|
||||
|
||||
signature = crypto.createHmac('sha512', secret).update(paramString).digest('hex');
|
||||
|
||||
return {
|
||||
Key: key,
|
||||
Sign: signature
|
||||
};
|
||||
};
|
||||
|
||||
// Currently, thisObject fails with `Error: CERT_UNTRUSTED`
|
||||
// Poloniex.STRICT_SSL can be set to `false` to avoid thisObject. Use with caution.
|
||||
// Will be removed in future, once thisObject is resolved.
|
||||
const STRICT_SSL = true;
|
||||
|
||||
// Prototype
|
||||
let thisObject = {
|
||||
|
||||
// Make an API request
|
||||
_request: function (options, callback) {
|
||||
if (!('headers' in options)) {
|
||||
options.headers = {};
|
||||
}
|
||||
|
||||
options.json = true;
|
||||
options.headers['User-Agent'] = USER_AGENT;
|
||||
options.strictSSL = STRICT_SSL;
|
||||
|
||||
request(options, function (err, response, body) {
|
||||
// Empty response
|
||||
if (!err && (typeof body === 'undefined' || body === null)) {
|
||||
err = 'Empty response';
|
||||
}
|
||||
|
||||
callback(err, body);
|
||||
});
|
||||
|
||||
return thisObject;
|
||||
},
|
||||
|
||||
// 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 thisObject._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,
|
||||
headers: _getPrivateHeaders(parameters)
|
||||
};
|
||||
|
||||
return thisObject._request(options, callback);
|
||||
},
|
||||
|
||||
/////
|
||||
// PUBLIC METHODS
|
||||
|
||||
returnTicker: function (callback) {
|
||||
return thisObject._public('returnTicker', callback);
|
||||
},
|
||||
|
||||
return24hVolume: function (callback) {
|
||||
return thisObject._public('return24hVolume', callback);
|
||||
},
|
||||
|
||||
returnOrderBook: function (currencyA, currencyB, depth, callback) {
|
||||
var parameters = {
|
||||
currencyPair: joinCurrencies(currencyA, currencyB),
|
||||
depth: depth
|
||||
};
|
||||
|
||||
return thisObject._public('returnOrderBook', parameters, callback);
|
||||
},
|
||||
|
||||
returnOrderBooks: function (depth, callback) {
|
||||
var parameters = {
|
||||
currencyPair: 'all',
|
||||
depth: depth
|
||||
};
|
||||
|
||||
return thisObject._public('returnOrderBook', parameters, callback);
|
||||
},
|
||||
|
||||
returnPublicTradeHistory: function (currencyA, currencyB, start, end, callback) {
|
||||
var parameters = {
|
||||
currencyPair: joinCurrencies(currencyA, currencyB),
|
||||
start: start,
|
||||
end: end
|
||||
};
|
||||
|
||||
return thisObject._public('returnTradeHistory', parameters, callback);
|
||||
},
|
||||
|
||||
returnChartData: function (currencyA, currencyB, period, start, end, callback) {
|
||||
var parameters = {
|
||||
currencyPair: joinCurrencies(currencyA, currencyB),
|
||||
period: period,
|
||||
start: start,
|
||||
end: end
|
||||
};
|
||||
|
||||
return thisObject._public('returnChartData', parameters, callback);
|
||||
},
|
||||
|
||||
returnCurrencies: function (callback) {
|
||||
return thisObject._public('returnCurrencies', callback);
|
||||
},
|
||||
|
||||
returnLoanOrders: function (currency, callback) {
|
||||
return thisObject._public('returnLoanOrders', { currency: currency }, callback);
|
||||
},
|
||||
|
||||
/////
|
||||
// PRIVATE METHODS
|
||||
|
||||
returnBalances: function (callback) {
|
||||
return thisObject._private('returnBalances', {}, callback);
|
||||
},
|
||||
|
||||
returnCompleteBalances: function (callback) {
|
||||
return thisObject._private('returnCompleteBalances', {}, callback);
|
||||
},
|
||||
|
||||
returnDepositAddresses: function (callback) {
|
||||
return thisObject._private('returnDepositAddresses', {}, callback);
|
||||
},
|
||||
|
||||
generateNewAddress: function (currency, callback) {
|
||||
return thisObject._private('returnDepositsWithdrawals', { currency: currency }, callback);
|
||||
},
|
||||
|
||||
returnDepositsWithdrawals: function (start, end, callback) {
|
||||
return thisObject._private('returnDepositsWithdrawals', { start: start, end: end }, callback);
|
||||
},
|
||||
|
||||
returnOpenOrders: function (currencyA, currencyB, callback) {
|
||||
var parameters = {
|
||||
currencyPair: joinCurrencies(currencyA, currencyB)
|
||||
};
|
||||
|
||||
return thisObject._private('returnOpenOrders', parameters, callback);
|
||||
},
|
||||
|
||||
returnTradeHistory: function (currencyA, currencyB, callback) {
|
||||
var parameters = {
|
||||
currencyPair: joinCurrencies(currencyA, currencyB)
|
||||
};
|
||||
|
||||
return thisObject._private('returnTradeHistory', parameters, callback);
|
||||
},
|
||||
|
||||
returnOrderTrades: function (orderNumber, callback) {
|
||||
var parameters = {
|
||||
orderNumber: orderNumber
|
||||
};
|
||||
|
||||
return thisObject._private('returnOrderTrades', parameters, callback);
|
||||
},
|
||||
|
||||
buy: function (currencyA, currencyB, rate, amount, callback) {
|
||||
var parameters = {
|
||||
currencyPair: joinCurrencies(currencyA, currencyB),
|
||||
rate: rate,
|
||||
amount: amount
|
||||
};
|
||||
|
||||
return thisObject._private('buy', parameters, callback);
|
||||
},
|
||||
|
||||
sell: function (currencyA, currencyB, rate, amount, callback) {
|
||||
var parameters = {
|
||||
currencyPair: joinCurrencies(currencyA, currencyB),
|
||||
rate: rate,
|
||||
amount: amount
|
||||
};
|
||||
|
||||
return thisObject._private('sell', parameters, callback);
|
||||
},
|
||||
|
||||
cancelOrder: function (currencyA, currencyB, orderNumber, callback) {
|
||||
var parameters = {
|
||||
currencyPair: joinCurrencies(currencyA, currencyB),
|
||||
orderNumber: orderNumber
|
||||
};
|
||||
|
||||
return thisObject._private('cancelOrder', parameters, callback);
|
||||
},
|
||||
|
||||
moveOrder: function (orderNumber, rate, amount, callback) {
|
||||
var parameters = {
|
||||
orderNumber: orderNumber,
|
||||
rate: rate,
|
||||
amount: amount ? amount : null
|
||||
};
|
||||
|
||||
return thisObject._private('moveOrder', parameters, callback);
|
||||
},
|
||||
|
||||
withdraw: function (currency, amount, address, callback) {
|
||||
var parameters = {
|
||||
currency: currency,
|
||||
amount: amount,
|
||||
address: address
|
||||
};
|
||||
|
||||
return thisObject._private('withdraw', parameters, callback);
|
||||
},
|
||||
|
||||
returnFeeInfo: function (callback) {
|
||||
return thisObject._private('returnFeeInfo', {}, callback);
|
||||
},
|
||||
|
||||
returnAvailableAccountBalances: function (account, callback) {
|
||||
var options = {};
|
||||
if (account) {
|
||||
options.account = account;
|
||||
}
|
||||
return thisObject._private('returnAvailableAccountBalances', options, callback);
|
||||
},
|
||||
|
||||
returnTradableBalances: function (callback) {
|
||||
return thisObject._private('returnTradableBalances', {}, callback);
|
||||
},
|
||||
|
||||
transferBalance: function (currency, amount, fromAccount, toAccount, callback) {
|
||||
var parameters = {
|
||||
currency: currency,
|
||||
amount: amount,
|
||||
fromAccount: fromAccount,
|
||||
toAccount: toAccount
|
||||
};
|
||||
|
||||
return thisObject._private('transferBalance', parameters, callback);
|
||||
},
|
||||
|
||||
returnMarginAccountSummary: function (callback) {
|
||||
return thisObject._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 thisObject._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 thisObject._private('marginSell', parameters, callback);
|
||||
},
|
||||
|
||||
getMarginPosition: function (currencyA, currencyB, callback) {
|
||||
var parameters = {
|
||||
currencyPair: joinCurrencies(currencyA, currencyB)
|
||||
};
|
||||
|
||||
return thisObject._private('getMarginPosition', parameters, callback);
|
||||
},
|
||||
|
||||
closeMarginPosition: function (currencyA, currencyB, callback) {
|
||||
var parameters = {
|
||||
currencyPair: joinCurrencies(currencyA, currencyB)
|
||||
};
|
||||
|
||||
return thisObject._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 thisObject._private('createLoanOffer', parameters, callback);
|
||||
},
|
||||
|
||||
cancelLoanOffer: function (orderNumber, callback) {
|
||||
var parameters = {
|
||||
orderNumber: orderNumber
|
||||
};
|
||||
|
||||
return thisObject._private('cancelLoanOffer', parameters, callback);
|
||||
},
|
||||
|
||||
returnOpenLoanOffers: function (callback) {
|
||||
return thisObject._private('returnOpenLoanOffers', {}, callback);
|
||||
},
|
||||
|
||||
returnActiveLoans: function (callback) {
|
||||
return thisObject._private('returnActiveLoans', {}, callback);
|
||||
},
|
||||
|
||||
returnLendingHistory: function (start, end, limit, callback) {
|
||||
var parameters = {
|
||||
start: start,
|
||||
end: end,
|
||||
limit: limit
|
||||
};
|
||||
|
||||
return thisObject._private('returnLendingHistory', parameters, callback);
|
||||
},
|
||||
|
||||
toggleAutoRenew: function (orderNumber, callback) {
|
||||
return thisObject._private('toggleAutoRenew', { orderNumber: orderNumber }, callback);
|
||||
},
|
||||
|
||||
analizeResponse: function (logger, exchangeErr, exchangeResponse, notOkCallBack, okCallBack) {
|
||||
|
||||
const FULL_LOG = true;
|
||||
const LOG_FILE_CONTENT = true;
|
||||
const MODULE_NAME = "Poloniex API Client";
|
||||
|
||||
/* This function analizes the different situations we might encounter trying to access Poloniex and returns appropiate standard errors. */
|
||||
|
||||
try {
|
||||
|
||||
let stringExchangeResponse = JSON.stringify(exchangeResponse);
|
||||
let stringExchangeErr = JSON.stringify(exchangeErr);
|
||||
|
||||
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] analizeResponse -> exchangeErr = " + stringExchangeErr); }
|
||||
if (LOG_FILE_CONTENT === true) { logger.write(MODULE_NAME, "[INFO] analizeResponse -> exchangeResponse = " + stringExchangeResponse); }
|
||||
|
||||
if (stringExchangeResponse === undefined) {
|
||||
|
||||
logger.write(MODULE_NAME, "[WARN] analizeResponse -> No response received from the exchange. Retrying Later.");
|
||||
|
||||
notOkCallBack(global.DEFAULT_RETRY_RESPONSE);
|
||||
return;
|
||||
}
|
||||
|
||||
if (stringExchangeErr.indexOf("ETIMEDOUT") > 0 ||
|
||||
stringExchangeErr.indexOf("ENOTFOUND") > 0 ||
|
||||
stringExchangeErr.indexOf("ECONNREFUSED") > 0 ||
|
||||
stringExchangeErr.indexOf("ESOCKETTIMEDOUT") > 0 ||
|
||||
stringExchangeErr.indexOf("ECONNRESET") > 0 ||
|
||||
(stringExchangeResponse !== undefined && (
|
||||
stringExchangeResponse.indexOf("Connection timed out") > 0 ||
|
||||
stringExchangeResponse.indexOf("Connection Error") > 0 ||
|
||||
stringExchangeResponse.indexOf("Bad gateway") > 0 ||
|
||||
stringExchangeResponse.indexOf("Nonce must be greater than") > 0 ||
|
||||
stringExchangeResponse.indexOf("Internal error. Please try again") > 0))) {
|
||||
|
||||
logger.write(MODULE_NAME, "[ERROR] analizeResponse -> Somethng bad happened while trying to access the Exchange API. Requesting new execution later.");
|
||||
logger.write(MODULE_NAME, "[ERROR] analizeResponse -> stringExchangeErr = " + stringExchangeErr);
|
||||
|
||||
notOkCallBack(global.DEFAULT_RETRY_RESPONSE);
|
||||
return;
|
||||
|
||||
} else {
|
||||
|
||||
if (JSON.stringify(exchangeResponse).indexOf("error") > 0) {
|
||||
|
||||
logger.write(MODULE_NAME, "[ERROR] analizeResponse -> Unexpected response from the Exchange.");
|
||||
logger.write(MODULE_NAME, "[ERROR] analizeResponse -> JSON.stringify(exchangeErr) = " + JSON.stringify(exchangeErr));
|
||||
logger.write(MODULE_NAME, "[ERROR] analizeResponse -> exchangeErr = " + exchangeErr);
|
||||
logger.write(MODULE_NAME, "[ERROR] analizeResponse -> exchangeResponse = " + exchangeResponse);
|
||||
logger.write(MODULE_NAME, "[ERROR] analizeResponse -> JSON.stringify(exchangeResponse) = " + JSON.stringify(exchangeResponse));
|
||||
|
||||
/* {"error":"Not enough USDT.","success":0}*/
|
||||
|
||||
if (JSON.stringify(exchangeResponse).indexOf("Not enough") > 0) {
|
||||
|
||||
let err = {
|
||||
resutl: global.CUSTOM_FAIL_RESPONSE.result,
|
||||
message: exchangeResponse.error
|
||||
};
|
||||
|
||||
notOkCallBack(err);
|
||||
|
||||
} else {
|
||||
|
||||
notOkCallBack(global.DEFAULT_FAIL_RESPONSE);
|
||||
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (exchangeErr) {
|
||||
|
||||
logger.write(MODULE_NAME, "[ERROR] analizeResponse -> Unexpected error trying to contact the Exchange.");
|
||||
logger.write(MODULE_NAME, "[ERROR] analizeResponse -> exchangeErr = " + exchangeErr);
|
||||
|
||||
notOkCallBack(global.DEFAULT_FAIL_RESPONSE);
|
||||
return;
|
||||
|
||||
} else {
|
||||
|
||||
logger.write(MODULE_NAME, "[INFO] analizeResponse -> No problem found.");
|
||||
okCallBack();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
} catch (err) {
|
||||
logger.write(MODULE_NAME, "[ERROR] analizeResponse -> err.message = " + err.message);
|
||||
notOkCallBack(global.DEFAULT_FAIL_RESPONSE);
|
||||
return;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return thisObject;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
+13
-2
@@ -39,6 +39,12 @@
|
||||
<Compile Include="DataSet.js">
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
<Compile Include="exchange\exchangeUtils.js" />
|
||||
<Compile Include="exchange\wrappers\beautifier.js" />
|
||||
<Compile Include="exchange\wrappers\binance.js" />
|
||||
<Compile Include="exchange\wrappers\binanceClient.js" />
|
||||
<Compile Include="exchange\wrappers\poloniex.js" />
|
||||
<Compile Include="exchange\wrappers\poloniexClient.js" />
|
||||
<Compile Include="Root.js">
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
@@ -52,13 +58,12 @@
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
<Compile Include="DebugLog.js" />
|
||||
<Compile Include="ExchangeAPI.js">
|
||||
<Compile Include="exchange\ExchangeAPI.js">
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
<Compile Include="IndicatorBotProcessMainLoop.js">
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
<Compile Include="PoloniexAPIClient.js" />
|
||||
<Compile Include="Run.js" />
|
||||
<Compile Include="StatusReport.js">
|
||||
<SubType>Code</SubType>
|
||||
@@ -70,8 +75,14 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include=".gitignore" />
|
||||
<Content Include="exchange\wrappers\binanceProperties.json" />
|
||||
<Content Include="exchange\wrappers\poloniexProperties.json" />
|
||||
<Content Include="this.config.json" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Folder Include="exchange\" />
|
||||
<Folder Include="exchange\wrappers\" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.Common.targets" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<!--Do not delete the following Import Project. While this appears to do nothing it is a marker for setting TypeScript properties before our import that depends on them.-->
|
||||
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\TypeScript\Microsoft.TypeScript.targets" Condition="False" />
|
||||
|
||||
@@ -29,17 +29,6 @@ exports.newRoot = function newRoot() {
|
||||
message: "Custom Message"
|
||||
};
|
||||
|
||||
/* Current state of the art fixed parameters */
|
||||
|
||||
global.MARKET = {
|
||||
assetA: "USDT",
|
||||
assetB: "BTC",
|
||||
};
|
||||
|
||||
/* Currently only one exchange is supported. */
|
||||
|
||||
global.EXCHANGE_NAME = "Poloniex";
|
||||
|
||||
/* This is the Execution Datetime */
|
||||
|
||||
global.EXECUTION_DATETIME = new Date();
|
||||
@@ -222,8 +211,9 @@ exports.newRoot = function newRoot() {
|
||||
for (let p = 0; p < global.PLATFORM_CONFIG.executionList.length; p++) {
|
||||
|
||||
let listItem = PLATFORM_CONFIG.executionList[p];
|
||||
let executionListItem = global.EXECUTION_CONFIG.executionList[p];
|
||||
|
||||
if (listItem.enabled !== "true") {
|
||||
if (listItem.enabled !== "true" || executionListItem.enabled !== "true") {
|
||||
|
||||
console.log(logDisplace + "Root : [INFO] start -> Skipping process for being disabled.");
|
||||
console.log(logDisplace + "Root : [INFO] start -> listItem.process = " + listItem.process);
|
||||
@@ -527,14 +517,14 @@ exports.newRoot = function newRoot() {
|
||||
|
||||
if (processConfig.startMode.live !== undefined) {
|
||||
|
||||
if (processConfig.startMode.live.run === "true") {
|
||||
if (processConfig.startMode.live.run === "true" && global.EXECUTION_CONFIG.startMode.live.run === "true") {
|
||||
|
||||
botConfig.startMode = "Live";
|
||||
|
||||
let month = pad((new Date()).getUTCMonth() + 1, 2);
|
||||
let year = (new Date()).getUTCFullYear();
|
||||
|
||||
if (processConfig.startMode.live.resumeExecution === "true") {
|
||||
if (processConfig.startMode.live.resumeExecution === "true" && global.EXECUTION_CONFIG.startMode.live.resumeExecution === "true") {
|
||||
botConfig.hasTheBotJustStarted = false;
|
||||
} else {
|
||||
botConfig.hasTheBotJustStarted = true;
|
||||
@@ -554,7 +544,7 @@ exports.newRoot = function newRoot() {
|
||||
|
||||
if (processConfig.startMode.backtest !== undefined) {
|
||||
|
||||
if (processConfig.startMode.backtest.run === "true") {
|
||||
if (processConfig.startMode.backtest.run === "true" && global.EXECUTION_CONFIG.startMode.backtest.run === "true") {
|
||||
|
||||
botConfig.startMode = "Backtest";
|
||||
botConfig.backtest = processConfig.startMode.backtest;
|
||||
@@ -582,15 +572,15 @@ exports.newRoot = function newRoot() {
|
||||
|
||||
if (processConfig.startMode.competition !== undefined) {
|
||||
|
||||
if (processConfig.startMode.competition.run === "true") {
|
||||
if (processConfig.startMode.competition.run === "true" && global.EXECUTION_CONFIG.startMode.competition.run === "true") {
|
||||
|
||||
botConfig.startMode = "Competition";
|
||||
botConfig.competition = processConfig.startMode.competition;
|
||||
|
||||
if (processConfig.startMode.competition.resumeExecution === "true") {
|
||||
botConfig.hasTheBotJustStarted = false;
|
||||
} else {
|
||||
if (processConfig.startMode.competition.resumeExecution === "false" && global.EXECUTION_CONFIG.startMode.competition.resumeExecution === "false") {
|
||||
botConfig.hasTheBotJustStarted = true;
|
||||
} else {
|
||||
botConfig.hasTheBotJustStarted = false;
|
||||
}
|
||||
|
||||
switch (botConfig.type) {
|
||||
|
||||
@@ -6,6 +6,13 @@ global.USER_LOGGED_IN = "Ciencias";
|
||||
global.DEV_TEAM = "AAMasters";
|
||||
global.AT_BREAKPOINT = false; // This is used only when running at the browser.
|
||||
|
||||
/* Default parameters can be changed by the execution configuration */
|
||||
global.EXCHANGE_NAME = "Poloniex";
|
||||
global.MARKET = {
|
||||
assetA: "USDT",
|
||||
assetB: "BTC"
|
||||
};
|
||||
|
||||
process.on('uncaughtException', function (err) {
|
||||
console.log('[INFO] Run -> uncaughtException -> err.message = ' + err.message);
|
||||
return;
|
||||
@@ -79,7 +86,7 @@ function readEmailConfiguration() {
|
||||
filePath = '../' + 'Email-Config' + '/' + 'Email.Config.json';
|
||||
global.EMAIL_CONFIG = JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
||||
|
||||
readExchangeAPIKey();
|
||||
readExecutionConfiguration();
|
||||
}
|
||||
catch (err) {
|
||||
console.log("[ERROR] Run -> readEmailConfiguration -> err = " + err.message);
|
||||
@@ -87,6 +94,29 @@ function readEmailConfiguration() {
|
||||
}
|
||||
}
|
||||
|
||||
function readExecutionConfiguration() {
|
||||
let filePath;
|
||||
try {
|
||||
let fs = require('fs');
|
||||
filePath = '../Execution-Config/Execution.Config.json';
|
||||
let executionProperties = JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
||||
global.DEV_TEAM = executionProperties.devTeam;
|
||||
global.EXCHANGE_NAME = executionProperties.exchangeName;
|
||||
global.MARKET = executionProperties.market;
|
||||
global.EXECUTION_CONFIG = {
|
||||
executionList: executionProperties.executionList,
|
||||
startMode: executionProperties.startMode
|
||||
};
|
||||
|
||||
readExchangeAPIKey();
|
||||
}
|
||||
catch (err) {
|
||||
console.log("[ERROR] readExecutionConfiguration -> err = " + err.message);
|
||||
console.log("[HINT] You need to have a file at this path -> " + filePath);
|
||||
}
|
||||
}
|
||||
|
||||
//TODO Move this method inside ExchangeAPI
|
||||
function readExchangeAPIKey() {
|
||||
|
||||
try {
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
run: run
|
||||
};
|
||||
|
||||
bot["botCache"] = new Map();
|
||||
let processConfig;
|
||||
let cloudStorage;
|
||||
let UI_COMMANDS;
|
||||
@@ -175,8 +176,7 @@
|
||||
/* We define here all the modules that the rest of the infraestructure, including the bots themselves can consume. */
|
||||
|
||||
const UTILITIES = require(ROOT_DIR + 'CloudUtilities');
|
||||
const POLONIEX_CLIENT_MODULE = require(ROOT_DIR + 'PoloniexAPIClient');
|
||||
const EXCHANGE_API = require(ROOT_DIR + 'ExchangeAPI');
|
||||
const EXCHANGE_API = require(ROOT_DIR + 'exchange/ExchangeAPI');
|
||||
const CONTEXT = require(ROOT_DIR + 'Context');
|
||||
const ASSISTANT = require(ROOT_DIR + 'Assistant');
|
||||
const STATUS_REPORT = require(ROOT_DIR + 'StatusReport');
|
||||
@@ -607,7 +607,7 @@
|
||||
|
||||
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> initializeExchangeAPI -> Entering function."); }
|
||||
|
||||
exchangeAPI = EXCHANGE_API.newExchangeAPI(bot, logger, POLONIEX_CLIENT_MODULE);
|
||||
exchangeAPI = EXCHANGE_API.newExchangeAPI(bot, logger);
|
||||
|
||||
exchangeAPI.initialize(onInizialized);
|
||||
|
||||
|
||||
@@ -0,0 +1,247 @@
|
||||
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;
|
||||
|
||||
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;
|
||||
|
||||
function initialize(callBackFunction) {
|
||||
try {
|
||||
|
||||
if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] initialize -> Entering function."); }
|
||||
|
||||
let exchange = global.EXCHANGE_NAME.toLowerCase() + 'Client.js';
|
||||
let api = require('./wrappers/' + exchange);
|
||||
if (!bot.instanceIndex)
|
||||
bot.instanceIndex = 0;
|
||||
apiClient = api.newAPIClient(global.EXCHANGE_KEYS[global.EXCHANGE_NAME][bot.instanceIndex].Key, global.EXCHANGE_KEYS[global.EXCHANGE_NAME][bot.instanceIndex].Secret, logger);
|
||||
|
||||
callBackFunction(global.DEFAULT_OK_RESPONSE);
|
||||
|
||||
} catch (err) {
|
||||
logger.write(MODULE_NAME, "[ERROR] initialize -> err = " + err.message);
|
||||
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -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
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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
@@ -0,0 +1,439 @@
|
||||
// Source: https://github.com/premasagar/poloniex.js
|
||||
// Documentation: https://poloniex.com/support/api/
|
||||
|
||||
module.exports = (function () {
|
||||
'use strict';
|
||||
|
||||
// Module dependencies
|
||||
var crypto = require('crypto'),
|
||||
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(key, secret) {
|
||||
// Generate headers signed by this user's key and secret.
|
||||
// The secret is encapsulated and never exposed
|
||||
this._getPrivateHeaders = function (parameters) {
|
||||
var paramString, signature;
|
||||
|
||||
if (!key || !secret) {
|
||||
throw 'Poloniex: Error. API key and secret required';
|
||||
}
|
||||
|
||||
// Convert to `arg1=foo&arg2=bar`
|
||||
paramString = Object.keys(parameters).map(function (param) {
|
||||
return encodeURIComponent(param) + '=' + encodeURIComponent(parameters[param]);
|
||||
}).join('&');
|
||||
|
||||
signature = crypto.createHmac('sha512', secret).update(paramString).digest('hex');
|
||||
|
||||
return {
|
||||
Key: key,
|
||||
Sign: signature
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
// 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,
|
||||
headers: this._getPrivateHeaders(parameters)
|
||||
};
|
||||
|
||||
return this._request(options, callback);
|
||||
},
|
||||
|
||||
/////
|
||||
// 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;
|
||||
})();
|
||||
@@ -0,0 +1,353 @@
|
||||
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 = "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(pKey, pSecret);
|
||||
|
||||
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
Generated
+1
-9
@@ -1,13 +1,5 @@
|
||||
{
|
||||
"name": "AACloud",
|
||||
"version": "1.0.0",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
"nodemailer": {
|
||||
"version": "4.6.7",
|
||||
"resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-4.6.7.tgz",
|
||||
"integrity": "sha512-GIAAYvs9XIP1fBa8wR89ukUh3yjL44pom5LKY5nTZcL+Zp9sRkqL8wgskyBQECQg9CPsDX/fjTZx8MNz20t0jA=="
|
||||
}
|
||||
}
|
||||
"lockfileVersion": 1
|
||||
}
|
||||
+1
-3
@@ -8,7 +8,5 @@
|
||||
},
|
||||
"author": "Luis-Fernando-Molina",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"nodemailer": "^4.6.7"
|
||||
}
|
||||
"dependencies": {}
|
||||
}
|
||||
Reference in new issue
Block a user