From f9ec627b573613ba25ed3151ef8a795f101f6c94 Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Tue, 6 Mar 2018 08:15:56 +0100 Subject: [PATCH] Context Module Introduced --- .vs/AACloudPlatform/v15/.suo | Bin 60416 -> 60416 bytes Context.js | 328 +++++++++++++++++++++++++++++++++-- 2 files changed, 310 insertions(+), 18 deletions(-) diff --git a/.vs/AACloudPlatform/v15/.suo b/.vs/AACloudPlatform/v15/.suo index f96bcadb7aecb8e20b9a7181018abfd3af7ad602..438e2df8dc776cb67261b96bf5ebde2ee6a3dda5 100644 GIT binary patch delta 775 zcmYk)O=uHA6bJCk+fCfqByCgEXwz?tMonzB(MBjz1nq|+iR55E4i%(G@eo2=8?81K z5$&P2nVX2{6RN0`HltU`$&(iqBnJf#y$PaFk0$;nq~Nf>ee>q?v23PbWD3S|udmr= zvl&nw(H)^-iF7#)Uj zP?znNF!P^YB~_A^Mb@gB*GFtuTZ<*SV!hjr=m9@?!393p3vQ@}J)le54Gpjhsvr&J z(h%FA5y}-+UhT*`;Hb?eA6P#7Ecz!_;w_?Hf2vI@)D*zT8Mp?6pf?U7o`q2vc6N)B zyyUDir;*E9XDq6(+io)$)cFC#IyeV6U=D7=EtrQ4@J`}W`pJ5x#C0|8c`j6=cT=#3 zxZc6dCXW!eQ+8}i^s7H(RfbH(yviH=DL7>DByoiJs)Kv=YPpc`D}U;TP&8B&xd9@% z7!Att3of~y{-RvzE!s%D7AwgF&zYPc88Qw4#mawA(L&f}Kw4%I$U6sooB zyTOwd-=_bLo+-NdF7X}WManH@44Ju~;Vkhz;>X0RrhF>*%U_R9O6TgR%0KKg^yCvq yd6M|0$uEf4DYr(X=uOe!HBw)ng-M0qe-^oSMR#qF{;|vLV15{VQq>=RxBLSFqW6ga delta 1528 zcmYk+eN5DK9KiAWe(!e&CkGtQPsgLA6p!*Cs9+a~N;wD2k!5SRm~+iPjA3J}b+{I_ zMt1NuSFJVuS(YwK?#sfm2z$~f zFG%-NS&Mb3hc)s}VjMQ8LRXoXRa;$ghP8s~a5ac6y^X81vFNt&8+=C+c3900q7z{V zhYO1k0XO0hg=lyXgIHK&flzi*#fl6IsIMw`G&$U@>xW2bN$d@=$=g zkPmB-LShk$QG#VCMH$Lbf#s+~6;@y+R-qa-SdF{k6a1L&A*sc^Sc5vOg|*mR@7D1? zPrGi9?-6~mUgh;{cK*$w2BKA`r&Ii;W;}jzQ|*fJiy1u=6Cuq+Ud}#ufxCxpFTjMV zayR%7lDvU8aR_hWZ5+lCyo+~m6xQ1B5s#rCL1+x%INrw~tbQL5Kg0=qgpV^<7eMa&*zQC9G3SZ+JoWi#_jWZa*cQ_B_i}d!~gJb!Q1K)K#(Dkugi{7XHEQg?3^HHlgQu&X_qe z_;9-v(`r|)-}wafuqb^i*C|xnhFBe&Hz|TGIZmBZbkS~>OLK=)?Wp!@Z}o&QlMT}? z&6QI5x>Iz@n#&@%UP>)H=Io|dD)Ume?tZe#;G{{NSrX4Lp#dN+g9kL+3S*3bP~1d`_C((%d7>=cU;$gFRjLh%lZE*5A}=JERVr zdfce%cyzlNkg6+Cs+xPFv~OfssNI{ARMPi{)yciB>iW?bbuE~$Th3+)B@Q&HsS`CS z`rNc?cx9=YdTq6G?c1V?&)# zR{1J&bnBJZg$fLocy=BidFh$qXU{y<*?0cw$>bX487fh2Klrq7Xed%mz3g@v?6zJ< IpPtVA7l*;Gb^rhX diff --git a/Context.js b/Context.js index b72c2c22e..64805b94f 100644 --- a/Context.js +++ b/Context.js @@ -1,19 +1,21 @@ -exports.newExchangeAPI = function newExchangeAPI(BOT) { +exports.newContext = function newContext(BOT) { /* - This module allows trading bots to have a context between executions. + This module allows trading bots to connect to the exchange and do trding operations on it. So far it can only work with Poloniex. + It deals with these 3 different files that helps the bot process remember where it is standing: + + 1. The Status Report. + 2. The Execution History. + 3. The Execution Context. */ - const MODULE_NAME = "Exchange API"; + const MODULE_NAME = "Context"; thisObject = { initialize: initialize, - getOpenPositions: getOpenPositions, - getExecutedTrades: getExecutedTrades, - putPosition: putPosition, - movePosition: movePosition + saveAll: saveAll }; let bot = BOT; @@ -22,33 +24,323 @@ const logger = DEBUG_MODULE.newDebugLog(); logger.fileName = MODULE_NAME; - const POLONIEX_CLIENT_MODULE = require('./Poloniex API Client'); - let poloniexApiClient; + let statusReport; // This is the typical Status Report that defines which was the last sucessfull execution and a few other pieces of info. + let executionHistory; // This is + let executionContext; // Here is the information of the last execution of this bot process. return thisObject; function initialize(callBackFunction) { try { + /* - let apiKey = readApiKey(); - poloniexApiClient = new POLONIEX_CLIENT_MODULE(apiKey.Key, apiKey.Secret); + Here we get the positions the bot did and that are recorded at the bot storage account. We will use them through out the rest + of the process. - function readApiKey() { + */ - try { - return JSON.parse(fs.readFileSync('../' + 'API-Keys' + '/' + EXCHANGE_NAME + '.json', 'utf8')); + getStatusReport(); + + function getStatusReport() { + + /* If the process run and was interrupted, there should be a status report that allows us to resume execution. */ + + let fileName = "Status.Report.json" + let filePath = EXCHANGE_NAME + "/" + bot.name + "/" + bot.dataSetVersion + "/Processes/" + bot.process; + + mariamAzureFileStorage.getTextFile(filePath, fileName, onFileReceived, true); + + function onFileReceived(text) { + + try { + + statusReport = JSON.parse(text); + + if (statusReport.lastExecution === undefined) { + + createConext(); + + } else { + + getExecutionHistory(); + + } + + } catch (err) { + + /* + + It might happen that the file content is corrupt or it does not exist. The bot can not run without a Status Report, + since it is risky to ignore its own history, so even for first time execution, a status report with the right format + is needed. + + */ + + const logText = "[ERROR] 'getStatusReport' - Bot cannot execute without a status report. ERROR : " + err.message; + logger.write(logText); + + } } - catch (err) { - logger.write("[ERROR] initialize -> readApiKey -> err = " + err); - callBackFunction("Operation Failed."); + } + + function getExecutionHistory() { + + let fileName = "Execution.History.json" + let filePath = EXCHANGE_NAME + "/" + bot.name + "/" + bot.dataSetVersion + "/Output/" + bot.process; + + mariamAzureFileStorage.getTextFile(filePath, fileName, onFileReceived, true); + + function onFileReceived(text) { + + try { + + executionHistory = JSON.parse(text); + getExecutionContext(); + + } catch (err) { + + /* + + It might happen that the file content is corrupt or it does not exist. The bot can not run without a Status Report, + since it is risky to ignore its own history, so even for first time execution, a status report with the right format + is needed. + + */ + + const logText = "[ERROR] 'getExecutionHistory' - Bot cannot execute without the Execution History. ERROR : " + err.message; + logger.write(logText); + + } } } + function getExecutionContext() { + + let date = new Date(statusReport.lastExecution); + + let fileName = "Execution.Context.json" + let dateForPath = date.getUTCFullYear() + '/' + utilities.pad(date.getUTCMonth() + 1, 2) + '/' + utilities.pad(date.getUTCDate(), 2) + '/' + utilities.pad(date.getUTCHours(), 2) + '/' + utilities.pad(date.getUTCMinutes(), 2); + let filePath = EXCHANGE_NAME + "/" + bot.name + "/" + bot.dataSetVersion + "/Output/" + bot.process + "/" + dateForPath; + + mariamAzureFileStorage.getTextFile(filePath, fileName, onFileReceived, true); + + function onFileReceived(text) { + + try { + + executionContext = JSON.parse(text); + + executionContext.transactions = []; // We record here the transactions that happened duting this execution. + + ordersExecutionCheck(); + + } catch (err) { + + /* + + It might happen that the file content is corrupt or it does not exist. The bot can not run without a Status Report, + since it is risky to ignore its own history, so even for first time execution, a status report with the right format + is needed. + + */ + + const logText = "[ERROR] 'getExecutionContext' - Bot cannot execute without the Execution Context. ERROR : " + err.message; + logger.write(logText); + + } + } + } + + function createConext() { + + /* + + When the bot is executed for the very first time, there are a few files that do not exist and need to be created, and that + is what we are going to do now. + + */ + + executionHistory = []; + + executionContext = { + investment: { + assetA: 0, + assetB: 0 + }, + availableBalance: { + assetA: 0, + assetB: 0 + }, + positions: [], + transactions: [] + }; + + getCandles(); + + } + } catch (err) { logger.write("[ERROR] initialize -> err = " + err); callBackFunction("Operation Failed."); } } -} \ No newline at end of file + + function saveAll(callBackFunction) { + + try { + + writeExecutionContext(); + + function writeExecutionContext() { + + if (LOG_INFO === true) { + logger.write("[INFO] Entering function 'writeExecutionContext'"); + } + + try { + + let fileName = "Execution.Context.json" + let dateForPath = processDatetime.getUTCFullYear() + '/' + utilities.pad(processDatetime.getUTCMonth() + 1, 2) + '/' + utilities.pad(processDatetime.getUTCDate(), 2) + '/' + utilities.pad(processDatetime.getUTCHours(), 2) + '/' + utilities.pad(processDatetime.getUTCMinutes(), 2); + let filePath = EXCHANGE_NAME + "/" + bot.name + "/" + bot.dataSetVersion + "/Output/" + bot.process + "/" + dateForPath; + + utilities.createFolderIfNeeded(filePath, mariamAzureFileStorage, onFolderCreated); + + function onFolderCreated() { + + try { + + let fileContent = JSON.stringify(executionContext); + + mariamAzureFileStorage.createTextFile(filePath, fileName, fileContent + '\n', onFileCreated); + + function onFileCreated() { + + if (LOG_INFO === true) { + logger.write("[INFO] 'writeExecutionContext' - Content written: " + fileContent); + } + + writeExucutionHistory(); + } + } + catch (err) { + const logText = "[ERROR] 'writeExecutionContext - onFolderCreated' - ERROR : " + err.message; + logger.write(logText); + } + } + + } + catch (err) { + const logText = "[ERROR] 'writeExecutionContext' - ERROR : " + err.message; + logger.write(logText); + } + } + + function writeExucutionHistory() { + + if (LOG_INFO === true) { + logger.write("[INFO] Entering function 'writeExucutionHistory'"); + } + + try { + + let fileName = "Execution.History.json" + let filePath = EXCHANGE_NAME + "/" + bot.name + "/" + bot.dataSetVersion + "/Output/" + bot.process; + + utilities.createFolderIfNeeded(filePath, mariamAzureFileStorage, onFolderCreated); + + function onFolderCreated() { + + try { + + let newRecord = [ + newHistoryRecord.date.valueOf(), + newHistoryRecord.rate, + newHistoryRecord.newPositions, + newHistoryRecord.newTrades, + newHistoryRecord.movedPositions + ]; + + executionHistory.push(newRecord); + + let fileContent = JSON.stringify(executionHistory); + + mariamAzureFileStorage.createTextFile(filePath, fileName, fileContent + '\n', onFileCreated); + + function onFileCreated() { + + if (LOG_INFO === true) { + logger.write("[INFO] 'writeExucutionHistory'"); + } + + writeStatusReport(); + } + } + catch (err) { + const logText = "[ERROR] 'writeExucutionHistory - onFolderCreated' - ERROR : " + err.message; + logger.write(logText); + } + } + + } + catch (err) { + const logText = "[ERROR] 'writeExucutionHistory' - ERROR : " + err.message; + logger.write(logText); + } + } + + function writeStatusReport() { + + if (LOG_INFO === true) { + logger.write("[INFO] Entering function 'writeStatusReport'"); + } + + try { + + let fileName = "Status.Report.json" + let filePath = EXCHANGE_NAME + "/" + bot.name + "/" + bot.dataSetVersion + "/Processes/" + bot.process; + + utilities.createFolderIfNeeded(filePath, mariamAzureFileStorage, onFolderCreated); + + function onFolderCreated() { + + try { + + statusReport.lastExecution = processDatetime; + + let fileContent = JSON.stringify(statusReport); + + mariamAzureFileStorage.createTextFile(filePath, fileName, fileContent + '\n', onFileCreated); + + function onFileCreated() { + + if (LOG_INFO === true) { + logger.write("[INFO] 'writeStatusReport' - Content written: " + fileContent); + } + + callBackFunction(true); // We tell the AA Platform that we request a regular execution and finish the bot s process. + return; + } + } + catch (err) { + const logText = "[ERROR] 'writeStatusReport - onFolderCreated' - ERROR : " + err.message; + logger.write(logText); + } + } + + } + catch (err) { + const logText = "[ERROR] 'writeStatusReport' - ERROR : " + err.message; + logger.write(logText); + } + } + + + } catch (err) { + logger.write("[ERROR] saveAll -> Error = " + err.message); + callBackFunction("Operation Failed."); + } + } + +}; \ No newline at end of file