balance and availableBalance handling

This commit is contained in:
Luis Fernando Molina committed 2018-03-10 11:58:25 +01:00
1 parent 4b6fddffff
commit 7b33ee1406
5 files changed
+242 -57

No files matched your search

Binary file not shown.
+204 -49
View File
@@ -14,7 +14,11 @@
initialize: initialize,
putPosition: putPosition,
movePosition: movePosition,
getPositions: getPositions
getPositions: getPositions,
getBalance: getBalance,
getAvailableBalance: getAvailableBalance,
getInvestment: getInvestment,
getProfits: getProfits
};
let bot = BOT;
@@ -29,8 +33,6 @@
let context;
let exchangeAPI;
const INITIAL_INVESTMENT = 0.001;
return thisObject;
function initialize(pContext, pExchangeAPI, callBackFunction) {
@@ -178,9 +180,12 @@
function loopBody() {
try {
let position = context.executionContext.positions[i];
for (let j = 0; j < exchangePositions.length; j++) {
if (context.executionContext.positions[i].id === exchangePositions[j].id) {
if (position.id === exchangePositions[j].id) {
/*
@@ -190,7 +195,7 @@
*/
if (context.executionContext.positions[i].amountB === parseFloat(exchangePositions[j].amountB)) {
if (position.amountB === parseFloat(exchangePositions[j].amountB)) {
/* Position is still there, untouched. Nothing to do here. */
@@ -199,7 +204,7 @@
} else {
getPositionTradesAtExchange(context.executionContext.positions[i].id, confirmOrderWasPartiallyExecuted);
getPositionTradesAtExchange(position.id, confirmOrderWasPartiallyExecuted);
return;
function confirmOrderWasPartiallyExecuted(pTrades) {
@@ -233,8 +238,8 @@
sumAssetA = sumAssetA + exchangePositions[j].fee;
if (
context.executionContext.positions[i].amountA !== sumAssetA ||
context.executionContext.positions[i].amountB !== sumAssetB
position.amountA !== sumAssetA ||
position.amountB !== sumAssetB
) {
logger.write("[ERROR] ordersExecutionCheck -> loopBody -> confirmOrderWasPartiallyExecuted -> Cannot be confirmed that a partially execution was done well.");
callBack(global.DEFAULT_FAIL_RESPONSE);
@@ -247,21 +252,15 @@
*/
context.executionContext.positions[i].amountA = exchangePositions[j].amountA;
context.executionContext.positions[i].amountB = exchangePositions[j].amountB;
context.executionContext.positions[i].date = (new Date(exchangePositions[j].date)).valueOf();
position.amountA = exchangePositions[j].amountA;
position.amountB = exchangePositions[j].amountB;
position.date = (new Date(exchangePositions[j].date)).valueOf();
for (k = 0; k < pTrades.length; k++) {
context.executionContext.positions[i].pTrades.push(pTrades[k]);
context.newHistoryRecord.newTrades++;
}
applyTradesToContext(pTrades);
let newTransaction = {
type: context.executionContext.positions[i].type + " partially executed",
position: context.executionContext.positions[i]
type: position.type + " partially executed",
position: position
};
context.executionContext.transactions.push(newTransaction);
@@ -282,7 +281,7 @@
/* Position not found: we need to know if the order was executed. */
getPositionTradesAtExchange(context.executionContext.positions[i].id, confirmOrderWasExecuted);
getPositionTradesAtExchange(position.id, confirmOrderWasExecuted);
function getPositionTradesAtExchange(pPositionId, innerCallBack) {
@@ -362,8 +361,8 @@
sumAssetA = sumAssetA + exchangePositions[j].fee;
if (
context.executionContext.positions[i].amountA !== sumAssetA ||
context.executionContext.positions[i].amountB !== sumAssetB
position.amountA !== sumAssetA ||
position.amountB !== sumAssetB
) {
logger.write("[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.");
callBack(global.DEFAULT_FAIL_RESPONSE);
@@ -376,19 +375,13 @@
*/
context.executionContext.positions[i].status = "executed";
position.status = "executed";
for (k = 0; k < pTrades.length; k++) {
context.executionContext.positions[i].pTrades.push(pTrades[k]);
context.newHistoryRecord.newTrades++;
}
applyTradesToContext(pTrades);
let newTransaction = {
type: context.executionContext.positions[i].type + " executed",
position: context.executionContext.positions[i]
type: position.type + " executed",
position: position
};
context.executionContext.transactions.push(newTransaction);
@@ -436,13 +429,54 @@
callBack(global.DEFAULT_OK_RESPONSE);
}
function applyTradesToContext(pTrades) {
for (k = 0; k < pTrades.length; k++) {
position.pTrades.push(pTrades[k]);
context.newHistoryRecord.newTrades++;
/* Calculate Balances */
if (trade.type === 'buy') {
context.executionContext.balance.assetA = context.executionContext.balance.assetA - trade.amountA;
context.executionContext.balance.assetB = context.executionContext.balance.assetB + trade.amountB;
context.executionContext.availableBalance.assetB = context.executionContext.availableBalance.assetB + trade.amountB;
}
if (trade.type === 'sell') {
context.executionContext.balance.assetA = context.executionContext.balance.assetA + trade.amountA;
context.executionContext.balance.assetB = context.executionContext.balance.assetB - trade.amountB;
context.executionContext.availableBalance.assetA = context.executionContext.availableBalance.assetA + trade.amountA;
}
}
/* Calculate Profits */
if (context.executionContext.investment.assetA > 0) {
context.executionContext.profits.assetA = (context.executionContext.balance.assetA - context.executionContext.investment.assetA) / context.executionContext.investment.assetA;
}
if (context.executionContext.investment.assetB > 0) {
context.executionContext.profits.assetB = (context.executionContext.balance.assetB - context.executionContext.investment.assetB) / context.executionContext.investment.assetB;
}
}
} catch (err) {
logger.write("[ERROR] ordersExecutionCheck -> err = " + err.message);
callBack(global.DEFAULT_FAIL_RESPONSE);
}
}
function putPosition(pType, pRate, pAmountA, pAmountB, callBack) {
function putPosition(pType, pRate, pAmountA, pAmountB, callBackFunction) {
try {
if (FULL_LOG === true) { logger.write("[INFO] putPosition -> Entering function."); }
@@ -451,6 +485,62 @@
if (FULL_LOG === true) { logger.write("[INFO] putPosition -> pAmountA = " + pAmountA); }
if (FULL_LOG === true) { logger.write("[INFO] putPosition -> pAmountB = " + pAmountB); }
/* Validations that the limits are not surpassed. */
if (pType === 'buy') {
if (pAmountA > context.executionContext.availableBalance.assetA) {
logger.write("[ERROR] putPosition -> Input Validations -> pAmountA is grater than the Available Balance.");
logger.write("[ERROR] putPosition -> Input Validations -> pAmountA = " + pAmountA);
logger.write("[ERROR] putPosition -> Input Validations -> Available Balance = " + context.executionContext.availableBalance.assetA);
let err = {
result: global.DEFAULT_FAIL_RESPONSE.result;
message: 'pAmountA is grater than the Available Balance.'
};
callBackFunction(err);
return;
}
}
if (pType === 'sell') {
if (pAmountB > context.executionContext.availableBalance.assetB) {
logger.write("[ERROR] putPosition -> Input Validations -> pAmountB is grater than the Available Balance.");
logger.write("[ERROR] putPosition -> Input Validations -> pAmountB = " + pAmountB);
logger.write("[ERROR] putPosition -> Input Validations -> Available Balance = " + context.executionContext.availableBalance.assetB);
let err = {
result: global.DEFAULT_FAIL_RESPONSE.result;
message: 'pAmountB is grater than the Available Balance.'
};
callBackFunction(err);
return;
}
}
if (pAmountB * pRate !== pAmountA) {
logger.write("[ERROR] putPosition -> Input Validations -> pAmountB * pRate !== pAmountA.");
logger.write("[ERROR] putPosition -> Input Validations -> pAmountB = " + pAmountA);
logger.write("[ERROR] putPosition -> Input Validations -> pAmountB = " + pAmountB);
logger.write("[ERROR] putPosition -> Input Validations -> pAmountB = " + pRate);
let err = {
result: global.DEFAULT_FAIL_RESPONSE.result;
message: 'pAmountB * pRate !== pAmountA'
};
callBackFunction(err);
return;
}
/* All validations passed, we proceed. */
exchangeAPI.putPosition(global.MARKET, pType, pRate, pAmountA, pAmountB, onResponse);
function onResponse(err, pPositionId) {
@@ -484,37 +574,50 @@
context.executionContext.transactions.push(newTransaction);
context.newHistoryRecord.newPositions++;
context.newHistoryRecord.rate = pRate;
callBack(global.DEFAULT_OK_RESPONSE);
recalculateRateAverages();
/* Recalculate Available Balances */
if (position.type === 'buy') {
context.executionContext.availableBalance.assetA = context.executionContext.availableBalance.assetA - trade.amountA;
}
if (position.type === 'sell') {
context.executionContext.availableBalance.assetB = context.executionContext.availableBalance.assetB - trade.amountB;
}
callBackFunction(global.DEFAULT_OK_RESPONSE);
return;
}
break;
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("[ERROR] putPosition -> onResponse -> Retry Later. Requesting Execution Retry.");
callBack(global.DEFAULT_RETRY_RESPONSE);
callBackFunction(global.DEFAULT_RETRY_RESPONSE);
return;
}
break;
case global.DEFAULT_FAIL_RESPONSE.result: { // This is an unexpected exception that we do not know how to handle.
logger.write("[ERROR] putPosition -> onResponse -> Operation Failed. Aborting the process.");
callBack(global.DEFAULT_FAIL_RESPONSE);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
return;
}
break;
}
} catch (err) {
logger.write("[ERROR] putPosition -> onResponse -> err = " + err.message);
callBack(global.DEFAULT_FAIL_RESPONSE);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
}
}
} catch (err) {
logger.write("[ERROR] putPosition -> err = " + err.message);
callBack(global.DEFAULT_FAIL_RESPONSE);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
}
}
function movePosition(pPosition, pNewRate, callBack) {
function movePosition(pPosition, pNewRate, callBackFunction) {
try {
if (FULL_LOG === true) { logger.write("[INFO] movePosition -> Entering function."); }
@@ -550,9 +653,9 @@
for (let i = 0; i < context.executionContext.positions.length; i++) {
if (context.executionContext.positions[i].id === pPosition.id) {
if (position.id === pPosition.id) {
context.executionContext.positions[i] = newPosition;
position = newPosition;
break;
}
@@ -567,36 +670,88 @@
context.executionContext.transactions.push(newTransaction);
context.newHistoryRecord.movedPositions++;
context.newHistoryRecord.rate = pNewRate;
callBack(global.DEFAULT_OK_RESPONSE);
recalculateRateAverages();
callBackFunction(global.DEFAULT_OK_RESPONSE);
}
break;
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("[ERROR] movePosition -> onResponse -> Retry Later. Requesting Execution Retry.");
callBack(global.DEFAULT_RETRY_RESPONSE);
callBackFunction(global.DEFAULT_RETRY_RESPONSE);
return;
}
break;
case global.DEFAULT_FAIL_RESPONSE.result: { // This is an unexpected exception that we do not know how to handle.
logger.write("[ERROR] movePosition -> onResponse -> Operation Failed. Aborting the process.");
callBack(global.DEFAULT_FAIL_RESPONSE);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
return;
}
break;
}
} catch (err) {
logger.write("[ERROR] movePosition -> onResponse -> err = " + err.message);
callBack(global.DEFAULT_FAIL_RESPONSE);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
}
}
} catch (err) {
logger.write("[ERROR] movePosition -> err = " + err.message);
callBack(global.DEFAULT_FAIL_RESPONSE);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
}
}
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. */
let sumBuyWeightedRates = 0;
let sumSellWeightedRates = 0;
let sumBuyWeights = 0;
let sumSellWeights = 0;
for (let i = 0; i < context.executionContext.positions.length; i++) {
let position = context.executionContext.positions[i];
if (position.type === 'buy') {
sumBuyWeightedRates = sumBuyWeightedRates + position.rate * position.amountB
sumBuyWeights = sumBuyWeights + position.amountB;
}
if (position.type === 'sell') {
sumSellWeightedRates = sumSellWeightedRates + position.rate * position.amountB
sumSellWeights = sumSellWeights + position.amountB;
}
}
context.newHistoryRecord.buyAvgRate = sumBuyWeightedRates / sumBuyWeights;
context.newHistoryRecord.sellAvgRate = sumSellWeightedRates / sumSellWeights;
}
function getPositions() {
return JSON.parse(JSON.stringify(context.executionContext.positions));
}
function getBalance() {
return JSON.parse(JSON.stringify(context.executionContext.balance));
}
function getAvailableBalance() {
return JSON.parse(JSON.stringify(context.executionContext.availableBalance));
}
function getInvestment() {
return JSON.parse(JSON.stringify(context.executionContext.investment));
}
function getProfits() {
return JSON.parse(JSON.stringify(context.executionContext.profits));
}
};
+27 -7
View File
@@ -38,10 +38,15 @@
executionContext: undefined, // Here is the business information of the last execution of this bot process.
newHistoryRecord : {
date: undefined,
rate: 0, // This will be used to know where to plot this information in the time line.
buyAvgRate: 0,
sellAvgRate: 0,
newPositions: 0,
newTrades: 0,
movedPositions: 0
movedPositions: 0,
profitsAssetA: 0,
profitsAssetB: 0,
profitsCombinedInA: 0,
profitsCombinedInB: 0
},
initialize: initialize,
saveThemAll: saveThemAll
@@ -316,11 +321,20 @@
thisObject.executionHistory = [];
thisObject.executionContext = {
investment: {
investment: { // This is used to calculate profits.
assetA: 0,
assetB: 0
assetB: global.INITIAL_INVESTMENT
},
availableBalance: {
balance: { // This is the total balance that includes positions at the order book + funds available to be traded.
assetA: 0,
assetB: global.INITIAL_INVESTMENT // It starts with the initial investment.
},
availableBalance: { // This is the balance the bot has at any moment in time available to be traded (not in positions at the order book).
assetA: 0,
assetB: global.INITIAL_INVESTMENT // It starts with the initial investment.
},
profitsAsset: global.PROFIT_BASE_ASSET,
profits: {
assetA: 0,
assetB: 0
},
@@ -467,10 +481,16 @@
let newRecord = [
thisObject.newHistoryRecord.date.valueOf(),
thisObject.newHistoryRecord.rate,
thisObject.newHistoryRecord.buyAvgRate,
thisObject.newHistoryRecord.sellAvgRate,
thisObject.newHistoryRecord.marketRate,
thisObject.newHistoryRecord.newPositions,
thisObject.newHistoryRecord.newTrades,
thisObject.newHistoryRecord.movedPositions
thisObject.newHistoryRecord.movedPositions,
thisObject.newHistoryRecord.profitsAssetA,
thisObject.newHistoryRecord.profitsAssetB,
thisObject.newHistoryRecord.profitsCombinedInA,
thisObject.newHistoryRecord.profitsCombinedInB
];
thisObject.executionHistory.push(newRecord);
+10
View File
@@ -127,6 +127,16 @@ global.dailyFilePeriods =
global.dailyFilePeriods = JSON.parse(global.dailyFilePeriods);
/*
In the current release, the platform is going to be used for the first competition, so we have a few parameters just for that. They will be removed later
once we advance into multiple competitions scheme.
*/
global.INITIAL_INVESTMENT = 0.0001;
global.PROFIT_BASE_ASSET = 'BTC';
/* Some LAGACY code starts here. Pending for clean up. */
const START_ALL_MONTHS = false;
+1 -1
View File
@@ -2,5 +2,5 @@
"bot": {
"path": "../Bots/AAMasters/AAMariam-Trading-Bot"
},
"stopGracefully": "false"
"stopGracefully": "true"
}