From 6549595802ba37aa19aee92a9f76505dffb6845b Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Sun, 24 Feb 2019 17:02:05 +0100 Subject: [PATCH] Initial commit --- .gitattributes | 2 + .gitignore | 11 + AAJason-Indicator-Bot.sln | 25 ++ Commons.js | 83 ++++ LICENSE | 24 + Multi-Period-Daily/User.Bot.js | 753 ++++++++++++++++++++++++++++++++ Multi-Period-Market/User.Bot.js | 593 +++++++++++++++++++++++++ Project.njsproj | 91 ++++ README.md | 7 + package.json | 13 + this.bot.config.json | 154 +++++++ 11 files changed, 1756 insertions(+) create mode 100644 .gitattributes create mode 100644 .gitignore create mode 100644 AAJason-Indicator-Bot.sln create mode 100644 Commons.js create mode 100644 LICENSE create mode 100644 Multi-Period-Daily/User.Bot.js create mode 100644 Multi-Period-Market/User.Bot.js create mode 100644 Project.njsproj create mode 100644 README.md create mode 100644 package.json create mode 100644 this.bot.config.json diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 000000000..dfe077042 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +# Auto detect text files and perform LF normalization +* text=auto diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..82c487bdd --- /dev/null +++ b/.gitignore @@ -0,0 +1,11 @@ + +.vs/* +obj/* + +bin/* + +*.config +*.log +*.cache +node_modules/* + diff --git a/AAJason-Indicator-Bot.sln b/AAJason-Indicator-Bot.sln new file mode 100644 index 000000000..da30e179e --- /dev/null +++ b/AAJason-Indicator-Bot.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.27130.2027 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9092AA53-FB77-4645-B42D-1CCCA6BD08BD}") = "Project", "Project.njsproj", "{AA2901BC-8F81-40F0-A402-5CA79A3F22AD}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {AA2901BC-8F81-40F0-A402-5CA79A3F22AD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AA2901BC-8F81-40F0-A402-5CA79A3F22AD}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AA2901BC-8F81-40F0-A402-5CA79A3F22AD}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AA2901BC-8F81-40F0-A402-5CA79A3F22AD}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {B4BDAEAF-E863-458A-87C1-CC6D8C490784} + EndGlobalSection +EndGlobal diff --git a/Commons.js b/Commons.js new file mode 100644 index 000000000..7436a4d56 --- /dev/null +++ b/Commons.js @@ -0,0 +1,83 @@ +exports.newCommons = function newCommons(bot, logger, UTILITIES) { + + const FULL_LOG = true; + const LOG_FILE_CONTENT = false; + + const MODULE_NAME = "Commons"; + + const GMT_SECONDS = ':00.000 GMT+0000'; + + let thisObject = { + initializeStorage: initializeStorage + }; + + let utilities = UTILITIES.newCloudUtilities(bot, logger); + + return thisObject; + + function initializeStorage(chrisStorage, jasonStorage, oliviaStorage, callBackFunction) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] initializeStorage -> Entering function."); } + + initializetomStorage(); + + function initializetomStorage() { + + jasonStorage.initialize(bot.devTeam, onBruceInizialized); + + function onBruceInizialized(err) { + + if (err.result === global.DEFAULT_OK_RESPONSE.result) { + + initializeChrisStorage(); + + } else { + logger.write(MODULE_NAME, "[ERROR] initializeStorage -> initializetomStorage -> onBruceInizialized -> err = " + err.message); + callBackFunction(err); + } + } + } + + function initializeChrisStorage() { + + chrisStorage.initialize(bot.devTeam, onChrisInizialized); + + function onChrisInizialized(err) { + + if (err.result === global.DEFAULT_OK_RESPONSE.result) { + + initializeOliviaStorage(); + + } else { + logger.write(MODULE_NAME, "[ERROR] initializeStorage -> initializeChrisStorage -> onChrisInizialized -> err = " + err.message); + callBackFunction(err); + } + } + } + + function initializeOliviaStorage() { + + oliviaStorage.initialize(bot.devTeam, onOliviaInizialized); + + function onOliviaInizialized(err) { + + if (err.result === global.DEFAULT_OK_RESPONSE.result) { + + callBackFunction(global.DEFAULT_OK_RESPONSE); + + } else { + logger.write(MODULE_NAME, "[ERROR] initializeStorage -> initializeChrisStorage -> onChrisInizialized -> err = " + err.message); + callBackFunction(err); + } + } + } + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] initializeStorage -> err = " + err.message); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + +}; \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 000000000..10e295746 --- /dev/null +++ b/LICENSE @@ -0,0 +1,24 @@ +Financial Beings License + +Collective Copyright by the Advanced Algos Community + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software including without limitation the rights to use, copy, modify, +merge, publish, distribute and sublicense copies of the Software, and to permit +persons to whom the Software is furnished to do so, subject to the following +conditions: + + 1. Using, copying, modifying, publishing, distributing and sublicensing rights + are restricted to the Advanced Algos Platform, meaning the rights granted can + only be exercised through the Advanced Algos Service. + + 2. The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/Multi-Period-Daily/User.Bot.js b/Multi-Period-Daily/User.Bot.js new file mode 100644 index 000000000..abcd97979 --- /dev/null +++ b/Multi-Period-Daily/User.Bot.js @@ -0,0 +1,753 @@ +exports.newUserBot = function newUserBot(bot, logger, COMMONS, UTILITIES, BLOB_STORAGE, FILE_STORAGE) { + + const FULL_LOG = true; + const INTENSIVE_LOG = false; + const LOG_FILE_CONTENT = false; + + const GMT_SECONDS = ':00.000 GMT+0000'; + const ONE_DAY_IN_MILISECONDS = 24 * 60 * 60 * 1000; + + const MODULE_NAME = "User Bot"; + + const EXCHANGE_NAME = "Poloniex"; + + const TRADES_FOLDER_NAME = "Trades"; + + const CANDLES_FOLDER_NAME = "Bands"; + const SIMULATED_TRADES_FOLDER_NAME = "Trading-Simulation"; + + const commons = COMMONS.newCommons(bot, logger, UTILITIES); + + thisObject = { + initialize: initialize, + start: start + }; + + let chrisStorage = BLOB_STORAGE.newBlobStorage(bot, logger); + let jasonStorage = BLOB_STORAGE.newBlobStorage(bot, logger); + + let utilities = UTILITIES.newCloudUtilities(bot, logger); + + let statusDependencies; + + return thisObject; + + function initialize(pStatusDependencies, pMonth, pYear, callBackFunction) { + + try { + + logger.fileName = MODULE_NAME; + logger.initialize(); + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] initialize -> Entering function."); } + + statusDependencies = pStatusDependencies; + + commons.initializeStorage(chrisStorage, jasonStorage, onInizialized); + + function onInizialized(err) { + + if (err.result === global.DEFAULT_OK_RESPONSE.result) { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] initialize -> onInizialized -> Initialization Succeed."); } + callBackFunction(global.DEFAULT_OK_RESPONSE); + + } else { + logger.write(MODULE_NAME, "[ERROR] initialize -> onInizialized -> err = " + err.message); + callBackFunction(err); + } + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] initialize -> err = " + err.message); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + /* + + This process is going to do the following: + + Read the bands from Chris and produce daily files with bollinger trades. + + */ + + function start(callBackFunction) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> Entering function."); } + + let market = global.MARKET; + + /* Context Variables */ + + let contextVariables = { + lastTradeFile: undefined, // Datetime of the last file files sucessfully produced by this process. + firstTradeFile: undefined, // Datetime of the first trade file in the whole market history. + maxTradeFile: undefined // Datetime of the last file available to be used as an input of this process. + }; + + let previousDay; // Holds the date of the previous day relative to the processing date. + let processDate; // Holds the processing date. + + getContextVariables(); + + function getContextVariables() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> getContextVariables -> Entering function."); } + + let thisReport; + let reportKey; + let statusReport; + + /* We look first for Charly in order to get when the market starts. */ + + reportKey = "AAMasters" + "-" + "AACharly" + "-" + "Poloniex-Historic-Trades" + "-" + "dataSet.V1"; + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> getContextVariables -> reportKey = " + reportKey); } + + statusReport = statusDependencies.statusReports.get(reportKey); + + if (statusReport === undefined) { // This means the status report does not exist, that could happen for instance at the begining of a month. + logger.write(MODULE_NAME, "[WARN] start -> getContextVariables -> Status Report does not exist. Retrying Later. "); + callBackFunction(global.DEFAULT_RETRY_RESPONSE); + return; + } + + if (statusReport.status === "Status Report is corrupt.") { + logger.write(MODULE_NAME, "[ERROR] start -> getContextVariables -> Can not continue because dependecy Status Report is corrupt. "); + callBackFunction(global.DEFAULT_RETRY_RESPONSE); + return; + } + + thisReport = statusDependencies.statusReports.get(reportKey).file; + + if (thisReport.lastFile === undefined) { + logger.write(MODULE_NAME, "[WARN] start -> getContextVariables -> Undefined Last File. -> reportKey = " + reportKey); + logger.write(MODULE_NAME, "[HINT] start -> getContextVariables -> It is too early too run this process since the trade history of the market is not there yet."); + + let customOK = { + result: global.CUSTOM_OK_RESPONSE.result, + message: "Dependency does not exist." + } + logger.write(MODULE_NAME, "[WARN] start -> getContextVariables -> customOK = " + customOK.message); + callBackFunction(customOK); + return; + } + + contextVariables.firstTradeFile = new Date(thisReport.lastFile.year + "-" + thisReport.lastFile.month + "-" + thisReport.lastFile.days + " " + thisReport.lastFile.hours + ":" + thisReport.lastFile.minutes + GMT_SECONDS); + + /* Second, we get the report from Chris, to know when the marted ends. */ + + reportKey = "AAMasters" + "-" + "AAChris" + "-" + "Multi-Period-Daily" + "-" + "dataSet.V1"; + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> getContextVariables -> reportKey = " + reportKey); } + + statusReport = statusDependencies.statusReports.get(reportKey); + + if (statusReport === undefined) { // This means the status report does not exist, that could happen for instance at the begining of a month. + logger.write(MODULE_NAME, "[WARN] start -> getContextVariables -> Status Report does not exist. Retrying Later. "); + callBackFunction(global.DEFAULT_RETRY_RESPONSE); + return; + } + + if (statusReport.status === "Status Report is corrupt.") { + logger.write(MODULE_NAME, "[ERROR] start -> getContextVariables -> Can not continue because dependecy Status Report is corrupt. "); + callBackFunction(global.DEFAULT_RETRY_RESPONSE); + return; + } + + thisReport = statusDependencies.statusReports.get(reportKey).file; + + if (thisReport.lastFile === undefined) { + logger.write(MODULE_NAME, "[WARN] start -> getContextVariables -> Undefined Last File. -> reportKey = " + reportKey); + + let customOK = { + result: global.CUSTOM_OK_RESPONSE.result, + message: "Dependency not ready." + } + logger.write(MODULE_NAME, "[WARN] start -> getContextVariables -> customOK = " + customOK.message); + callBackFunction(customOK); + return; + } + + contextVariables.maxTradeFile = new Date(thisReport.lastFile.valueOf()); + + /* Finally we get our own Status Report. */ + + reportKey = "AAMasters" + "-" + "AAJason" + "-" + "Multi-Period-Daily" + "-" + "dataSet.V1"; + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> getContextVariables -> reportKey = " + reportKey); } + + statusReport = statusDependencies.statusReports.get(reportKey); + + if (statusReport === undefined) { // This means the status report does not exist, that could happen for instance at the begining of a month. + logger.write(MODULE_NAME, "[WARN] start -> getContextVariables -> Status Report does not exist. Retrying Later. "); + callBackFunction(global.DEFAULT_RETRY_RESPONSE); + return; + } + + if (statusReport.status === "Status Report is corrupt.") { + logger.write(MODULE_NAME, "[ERROR] start -> getContextVariables -> Can not continue because self dependecy Status Report is corrupt. Aborting Process."); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return; + } + + thisReport = statusDependencies.statusReports.get(reportKey).file; + + if (thisReport.lastFile !== undefined) { + + contextVariables.lastTradeFile = new Date(thisReport.lastFile); + + /* + The trades objects needs to be calculated over more than one day in some situations. In order to simplify the calculations we will + always load the last 2 days of bands, and run the calculations on that array. + + 1. So first we will load the bands file of processDay -1. + 2. Secondly we will load the bands file of processDay. + + After reading these two files we will add all bands to a unique array. + */ + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> getContextVariables -> thisReport.lastFile !== undefined"); } + + buildTrades(); + return; + + } else { + + /* + In the case when there is no status report, we take the date of the file with the first trades as the begining of the market. Then we will + go one day further in time, so that the previous day does fine a file at the begining of the market. + */ + + contextVariables.lastTradeFile = new Date(contextVariables.firstTradeFile.getUTCFullYear() + "-" + (contextVariables.firstTradeFile.getUTCMonth() + 1) + "-" + contextVariables.firstTradeFile.getUTCDate() + " " + "00:00" + GMT_SECONDS); + contextVariables.lastTradeFile = new Date(contextVariables.lastTradeFile.valueOf() + ONE_DAY_IN_MILISECONDS); + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> getContextVariables -> thisReport.lastFile === undefined"); } + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> getContextVariables -> contextVariables.lastTradeFile = " + contextVariables.lastTradeFile); } + + buildTrades(); + return; + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> getContextVariables -> err = " + err.message); + if (err.message === "Cannot read property 'file' of undefined") { + logger.write(MODULE_NAME, "[HINT] start -> getContextVariables -> Check the bot configuration to see if all of its statusDependencies declarations are correct. "); + logger.write(MODULE_NAME, "[HINT] start -> getContextVariables -> Dependencies loaded -> keys = " + JSON.stringify(statusDependencies.keys)); + } + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function buildTrades() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildTrades -> Entering function."); } + + let n; + processDate = new Date(contextVariables.lastTradeFile.valueOf() - ONE_DAY_IN_MILISECONDS); // Go back one day to start well when we advance time at the begining of the loop. + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildTrades -> processDate = " + processDate); } + + advanceTime(); + + function advanceTime() { + + try { + + logger.newInternalLoop(bot.codeName, bot.process); + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildTrades -> advanceTime -> Entering function."); } + + processDate = new Date(processDate.valueOf() + ONE_DAY_IN_MILISECONDS); + previousDay = new Date(processDate.valueOf() - ONE_DAY_IN_MILISECONDS); + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildTrades -> advanceTime -> processDate = " + processDate); } + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildTrades -> advanceTime -> previousDay = " + previousDay); } + + /* Validation that we are not going past the head of the market. */ + + if (processDate.valueOf() > contextVariables.maxTradeFile.valueOf()) { + + const logText = "Head of the market found @ " + previousDay.getUTCFullYear() + "/" + (previousDay.getUTCMonth() + 1) + "/" + previousDay.getUTCDate() + "."; + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildTrades -> advanceTime -> " + logText); } + + callBackFunction(global.DEFAULT_OK_RESPONSE); + return; + + } + + periodsLoop(); + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> buildTrades -> advanceTime -> err = " + err.message); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function periodsLoop() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildTrades -> periodsLoop -> Entering function."); } + + /* + + We will iterate through all posible timePeriods. + + */ + + n = 0 // loop Variable representing each possible period as defined at the periods array. + + loopBody(); + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> buildTrades -> periodsLoop -> err = " + err.message); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function loopBody() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildTrades -> loopBody -> Entering function."); } + + const timePeriod = global.dailyFilePeriods[n][1]; + + let bands = []; // Here we will put all the bands of the 2 files read. + + let previousDayFile; + let processDayFile; + + getPreviousDayFile(); + + function getPreviousDayFile() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildTrades -> loopBody -> getPreviousDayFile -> Entering function."); } + + let dateForPath = previousDay.getUTCFullYear() + '/' + utilities.pad(previousDay.getUTCMonth() + 1, 2) + '/' + utilities.pad(previousDay.getUTCDate(), 2); + let fileName = market.assetA + '_' + market.assetB + ".json" + + let filePathRoot = bot.devTeam + "/" + "AAChris" + "." + bot.version.major + "." + bot.version.minor + "/" + global.PLATFORM_CONFIG.codeName + "." + global.PLATFORM_CONFIG.version.major + "." + global.PLATFORM_CONFIG.version.minor + "/" + global.EXCHANGE_NAME + "/" + bot.dataSetVersion; + let filePath = filePathRoot + "/Output/" + CANDLES_FOLDER_NAME + '/' + "Multi-Period-Daily" + "/" + timePeriod + "/" + dateForPath; + + chrisStorage.getTextFile(filePath, fileName, onCurrentDayFileReceived, true); + + function onCurrentDayFileReceived(err, text) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildTrades -> loopBody -> getPreviousDayFile -> onCurrentDayFileReceived -> Entering function."); } + if (LOG_FILE_CONTENT === true) { logger.write(MODULE_NAME, "[INFO] start -> buildTrades -> loopBody -> getPreviousDayFile -> onCurrentDayFileReceived -> text = " + text); } + + previousDayFile = JSON.parse(text); + getProcessDayFile() + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> buildTrades -> periodsLoop -> loopBody -> getPreviousDayFile -> onCurrentDayFileReceived -> err = " + err.message); + logger.write(MODULE_NAME, "[ERROR] start -> buildTrades -> periodsLoop -> loopBody -> getPreviousDayFile -> onCurrentDayFileReceived -> filePath = " + filePath); + logger.write(MODULE_NAME, "[ERROR] start -> buildTrades -> periodsLoop -> loopBody -> getPreviousDayFile -> onCurrentDayFileReceived -> market = " + market.assetA + '_' + market.assetB); + + callBackFunction(global.DEFAULT_RETRY_RESPONSE); + } + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> buildTrades -> periodsLoop -> loopBody -> getPreviousDayFile -> err = " + err.message); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function getProcessDayFile() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildTrades -> loopBody -> getProcessDayFile -> Entering function."); } + + let dateForPath = processDate.getUTCFullYear() + '/' + utilities.pad(processDate.getUTCMonth() + 1, 2) + '/' + utilities.pad(processDate.getUTCDate(), 2); + let fileName = market.assetA + '_' + market.assetB + ".json" + + let filePathRoot = bot.devTeam + "/" + "AAChris" + "." + bot.version.major + "." + bot.version.minor + "/" + global.PLATFORM_CONFIG.codeName + "." + global.PLATFORM_CONFIG.version.major + "." + global.PLATFORM_CONFIG.version.minor + "/" + global.EXCHANGE_NAME + "/" + bot.dataSetVersion; + let filePath = filePathRoot + "/Output/" + CANDLES_FOLDER_NAME + '/' + "Multi-Period-Daily" + "/" + timePeriod + "/" + dateForPath; + + chrisStorage.getTextFile(filePath, fileName, onCurrentDayFileReceived, true); + + function onCurrentDayFileReceived(err, text) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildTrades -> loopBody -> getProcessDayFile -> onCurrentDayFileReceived -> Entering function."); } + if (LOG_FILE_CONTENT === true) { logger.write(MODULE_NAME, "[INFO] start -> buildTrades -> loopBody -> getProcessDayFile -> onCurrentDayFileReceived -> text = " + text); } + + processDayFile = JSON.parse(text); + buildTrades(); + + } catch (err) { + + if (processDate.valueOf() > contextVariables.maxTradeFile.valueOf()) { + + processDayFile = []; // we are past the head of the market, then no worries if this file is non existent. + buildTrades(); + + } else { + + logger.write(MODULE_NAME, "[ERROR] start -> buildTrades -> periodsLoop -> loopBody -> getProcessDayFile -> onCurrentDayFileReceived -> err = " + err.message); + logger.write(MODULE_NAME, "[ERROR] start -> buildTrades -> periodsLoop -> loopBody -> getProcessDayFile -> onCurrentDayFileReceived -> filePath = " + filePath); + logger.write(MODULE_NAME, "[ERROR] start -> buildTrades -> periodsLoop -> loopBody -> getProcessDayFile -> onCurrentDayFileReceived -> market = " + market.assetA + '_' + market.assetB); + + callBackFunction(global.DEFAULT_RETRY_RESPONSE); + return; + } + } + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> buildTrades -> periodsLoop -> loopBody -> getProcessDayFile -> err = " + err.message); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function buildTrades() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildTrades -> loopBody -> buildTrades -> Entering function."); } + + addBandsToSingleArray(previousDayFile); + addBandsToSingleArray(processDayFile); + calculateTrades(); + + function addBandsToSingleArray(bandsFile) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildTrades -> loopBody -> buildTrades -> addBandsToSingleArray -> Entering function."); } + + for (let i = 0; i < bandsFile.length; i++) { + + let band = { + open: undefined, + close: undefined, + min: 10000000000000, + max: 0, + begin: undefined, + end: undefined, + direction: undefined + }; + + band.min = bandsFile[i][0]; + band.max = bandsFile[i][1]; + + band.open = bandsFile[i][2]; + band.close = bandsFile[i][3]; + + band.begin = bandsFile[i][4]; + band.end = bandsFile[i][5]; + + if (band.open > band.close) { band.direction = 'down'; } + if (band.open < band.close) { band.direction = 'up'; } + if (band.open === band.close) { band.direction = 'side'; } + + bands.push(band); + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> buildTrades -> periodsLoop -> loopBody -> buildTrades -> addBandsToSingleArray -> err = " + err.message); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return; + } + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> buildTrades -> periodsLoop -> loopBody -> buildTrades -> err = " + err.message); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return; + } + } + + function calculateTrades() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildTrades -> loopBody -> calculateTrades -> Entering function."); } + + let tradesArray = []; + let numberOfPeriods = 20; + + /* Building trades */ + + let trade; + + for (let i = numberOfPeriods - 1; i < bands.length; i++) { // Go through all the bands to generate a trade segment for each of them. + + let movingAverage = 0; + for (let j = i - numberOfPeriods + 1; j < i + 1; j++) { // go through the last n bands to calculate the moving average. + movingAverage = movingAverage + bands[j].close; + } + movingAverage = movingAverage / numberOfPeriods; + + let standardDeviation = 0; + for (let j = i - numberOfPeriods + 1; j < i + 1; j++) { // go through the last n bands to calculate the standard deviation. + standardDeviation = standardDeviation + Math.pow(bands[j].close - movingAverage, 2); + } + standardDeviation = standardDeviation / numberOfPeriods; + standardDeviation = Math.sqrt(standardDeviation); + + trade = { + begin: bands[i].begin, + end: bands[i].end, + movingAverage: movingAverage, + standardDeviation: standardDeviation + }; + + /* Will only add to the array the trades of the current day */ + + if (trade.begin >= processDate.valueOf()) { tradesArray.push(trade); } + + } + + writeFile(tradesArray); + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> buildTrades -> periodsLoop -> loopBody -> calculateTrades -> err = " + err.message); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return; + } + } + + function writeFile(pTrades, callback) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildTrades -> loopBody -> writeTradeTradesFile -> writeFile -> Entering function."); } + + let separator = ""; + let fileRecordCounter = 0; + + let fileContent = ""; + + for (i = 0; i < pTrades.length; i++) { + + let trade = pTrades[i]; + + fileContent = fileContent + separator + '[' + + trade.begin + "," + + trade.end + "," + + trade.movingAverage + "," + + trade.standardDeviation + "]"; + + if (separator === "") { separator = ","; } + + fileRecordCounter++; + + } + + fileContent = "[" + fileContent + "]"; + + let dateForPath = processDate.getUTCFullYear() + '/' + utilities.pad(processDate.getUTCMonth() + 1, 2) + '/' + utilities.pad(processDate.getUTCDate(), 2); + let fileName = '' + market.assetA + '_' + market.assetB + '.json'; + + let filePathRoot = bot.devTeam + "/" + bot.codeName + "." + bot.version.major + "." + bot.version.minor + "/" + global.PLATFORM_CONFIG.codeName + "." + global.PLATFORM_CONFIG.version.major + "." + global.PLATFORM_CONFIG.version.minor + "/" + global.EXCHANGE_NAME + "/" + bot.dataSetVersion; + let filePath = filePathRoot + "/Output/" + SIMULATED_TRADES_FOLDER_NAME + "/" + bot.process + "/" + timePeriod + "/" + dateForPath; + + jasonStorage.createTextFile(filePath, fileName, fileContent + '\n', onFileCreated); + + function onFileCreated(err) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildTrades -> loopBody -> writeTradeTradesFile -> writeFile -> onFileCreated -> Entering function."); } + + if (LOG_FILE_CONTENT === true) { + logger.write(MODULE_NAME, "[INFO] start -> buildTrades -> loopBody -> writeTradeTradesFile -> writeFile -> onFileCreated -> Content written = " + fileContent); + } + + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + logger.write(MODULE_NAME, "[ERROR] start -> buildTrades -> loopBody -> writeTradeTradesFile -> writeFile -> onFileCreated -> err = " + err.message); + callBackFunction(err); + return; + } + + const logText = "[WARN] Finished with File @ " + market.assetA + "_" + market.assetB + ", " + fileRecordCounter + " records inserted into " + filePath + "/" + fileName + ""; + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildTrades -> loopBody -> writeTradeTradesFile -> writeFile -> onFileCreated -> " + logText); } + + controlLoop(); + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> buildTrades -> periodsLoop -> loopBody -> writeTradeTradesFile -> writeFile -> onFileCreated -> err = " + err.message); + callBackFunction(global.DEFAULT_RETRY_RESPONSE); + } + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> buildTrades -> periodsLoop -> loopBody -> writeTradeTradesFile -> writeFile -> err = " + err.message); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + + + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> buildTrades -> periodsLoop -> loopBody -> err = " + err.message); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function controlLoop() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildTrades -> controlLoop -> Entering function."); } + + n++; + + if (n < global.dailyFilePeriods.length) { + + loopBody(); + + } else { + + n = 0; + + writeDataRanges(onWritten); + + function onWritten(err) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildTrades -> controlLoop -> onWritten -> Entering function."); } + + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + logger.write(MODULE_NAME, "[ERROR] start -> buildTrades -> controlLoop -> onWritten -> err = " + err.message); + callBackFunction(err); + return; + } + + writeStatusReport(processDate, advanceTime); + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> buildTrades -> periodsLoop -> controlLoop -> onWritten -> err = " + err.message); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> buildTrades -> periodsLoop -> controlLoop -> err = " + err.message); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + } + + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> buildTrades -> err.message = " + err.message); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function writeDataRanges(callBack) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeDataRanges -> Entering function."); } + + writeDataRange(contextVariables.firstTradeFile, processDate, SIMULATED_TRADES_FOLDER_NAME, onTradesTradesDataRangeWritten); + + function onTradesTradesDataRangeWritten(err) { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeDataRanges -> Entering function."); } + + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + logger.write(MODULE_NAME, "[ERROR] writeDataRanges -> writeDataRanges -> onTradesTradesDataRangeWritten -> err = " + err.message); + callBack(err); + return; + } + + callBack(global.DEFAULT_OK_RESPONSE); + + } + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> writeDataRanges -> err = " + err.message); + callBack(global.DEFAULT_FAIL_RESPONSE); + } + + } + + function writeDataRange(pBegin, pEnd, pProductFolder, callBack) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeDataRange -> Entering function."); } + + let dataRange = { + begin: pBegin.valueOf(), + end: pEnd.valueOf() + }; + + let fileContent = JSON.stringify(dataRange); + + let fileName = 'Data.Range.' + market.assetA + '_' + market.assetB + '.json'; + let filePath = bot.filePathRoot + "/Output/" + pProductFolder + "/" + bot.process; + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeDataRange -> fileName = " + fileName); } + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeDataRange -> filePath = " + filePath); } + + jasonStorage.createTextFile(filePath, fileName, fileContent + '\n', onFileCreated); + + function onFileCreated(err) { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeDataRange -> onFileCreated -> Entering function."); } + + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + logger.write(MODULE_NAME, "[ERROR] start -> writeDataRange -> onFileCreated -> err = " + err.message); + callBack(err); + return; + } + + if (LOG_FILE_CONTENT === true) { + logger.write(MODULE_NAME, "[INFO] start -> writeDataRange -> onFileCreated -> Content written = " + fileContent); + } + + callBack(global.DEFAULT_OK_RESPONSE); + } + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> writeDataRange -> err = " + err.message); + callBack(global.DEFAULT_FAIL_RESPONSE); + } + } + + function writeStatusReport(lastFileDate, callBack) { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeStatusReport -> Entering function."); } + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeStatusReport -> lastFileDate = " + lastFileDate); } + + try { + + let reportKey = "AAMasters" + "-" + "AAJason" + "-" + "Multi-Period-Daily" + "-" + "dataSet.V1"; + let thisReport = statusDependencies.statusReports.get(reportKey); + + thisReport.file.lastExecution = bot.processDatetime; + thisReport.file.lastFile = lastFileDate; + thisReport.save(callBack); + + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> writeStatusReport -> err = " + err.message); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> err.message = " + err.message); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } +}; diff --git a/Multi-Period-Market/User.Bot.js b/Multi-Period-Market/User.Bot.js new file mode 100644 index 000000000..a2da081dd --- /dev/null +++ b/Multi-Period-Market/User.Bot.js @@ -0,0 +1,593 @@ +exports.newUserBot = function newUserBot(bot, logger, COMMONS, UTILITIES, BLOB_STORAGE, FILE_STORAGE) { + + const FULL_LOG = true; + const LOG_FILE_CONTENT = false; + + const MODULE_NAME = "User Bot"; + + const EXCHANGE_NAME = "Poloniex"; + + const PERCENTAGE_BANDWIDTH_FOLDER_NAME = "Percentage-Bandwidth"; + const CANDLES_FOLDER_NAME = "Candles"; + const SIMULATED_RECORDS_FOLDER_NAME = "Trading-Simulation"; + + const commons = COMMONS.newCommons(bot, logger, UTILITIES); + + thisObject = { + initialize: initialize, + start: start + }; + + let chrisStorage = BLOB_STORAGE.newBlobStorage(bot, logger); + let oliviaStorage = BLOB_STORAGE.newBlobStorage(bot, logger); + let jasonStorage = BLOB_STORAGE.newBlobStorage(bot, logger); + + let utilities = UTILITIES.newCloudUtilities(bot, logger); + + let statusDependencies; + + return thisObject; + + function initialize(pStatusDependencies, pMonth, pYear, callBackFunction) { + + try { + + logger.fileName = MODULE_NAME; + logger.initialize(); + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] initialize -> Entering function."); } + + statusDependencies = pStatusDependencies; + + commons.initializeStorage(chrisStorage, jasonStorage, oliviaStorage, onInizialized); + + function onInizialized(err) { + + if (err.result === global.DEFAULT_OK_RESPONSE.result) { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] initialize -> onInizialized -> Initialization Succeed."); } + callBackFunction(global.DEFAULT_OK_RESPONSE); + + } else { + logger.write(MODULE_NAME, "[ERROR] initialize -> onInizialized -> err = " + err.message); + callBackFunction(err); + } + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] initialize -> err = " + err.message); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + + + function start(callBackFunction) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> Entering function."); } + + let market = global.MARKET; + + buildRecords(); + + function buildRecords() { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildRecords -> Entering function."); } + + try { + + let n; + + periodsLoop(); + + function periodsLoop() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildRecords -> periodsLoop -> Entering function."); } + + /* + + We will iterate through all posible periods. + + */ + + n = 0 // loop Variable representing each possible period as defined at the periods array. + + loopBody(); + + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> buildRecords -> periodsLoop -> err = " + err.message); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function loopBody() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildRecords -> loopBody -> Entering function."); } + + const outputPeriod = global.marketFilesPeriods[n][0]; + const timePeriod = global.marketFilesPeriods[n][1]; + + let marketFile; + + let percentgeBandwidthArray = new Map(); + let candles = []; + let recordsArray = []; + + nextChrisFile(); + + function nextChrisFile() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildRecords -> loopBody -> nextChrisFile -> Entering function."); } + + let fileName = market.assetA + '_' + market.assetB + ".json"; + + let filePathRoot = bot.devTeam + "/" + "AAChris" + "." + bot.version.major + "." + bot.version.minor + "/" + global.PLATFORM_CONFIG.codeName + "." + global.PLATFORM_CONFIG.version.major + "." + global.PLATFORM_CONFIG.version.minor + "/" + global.EXCHANGE_NAME + "/" + bot.dataSetVersion; + let filePath = filePathRoot + "/Output/" + PERCENTAGE_BANDWIDTH_FOLDER_NAME + "/" + "Multi-Period-Market" + "/" + timePeriod; + + chrisStorage.getTextFile(filePath, fileName, onFileReceived, true); + + function onFileReceived(err, text) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildRecords -> loopBody -> nextChrisFile -> onFileReceived -> Entering function."); } + if (LOG_FILE_CONTENT === true) { logger.write(MODULE_NAME, "[INFO] start -> buildRecords -> loopBody -> nextChrisFile -> onFileReceived -> text = " + text); } + + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + + logger.write(MODULE_NAME, "[ERROR] start -> buildRecords -> loopBody -> nextChrisFile -> onFileReceived -> err = " + err.message); + callBackFunction(err); + return; + + } + + marketFile = JSON.parse(text); + + buildPercentgeBandwidthArray(); + + + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> buildRecords -> loopBody -> nextChrisFile -> onFileReceived -> err = " + err.message); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> buildRecords -> loopBody -> nextChrisFile -> err = " + err.message); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function buildPercentgeBandwidthArray() { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildRecords -> loopBody -> buildPercentgeBandwidthArray -> Entering function."); } + + try { + + for (let i = 0; i < marketFile.length; i++) { + + let percentgeBandwidth = { + begin: marketFile[i][0], + end: marketFile[i][1], + value: marketFile[i][2], + movingAverage: marketFile[i][3] + }; + + percentgeBandwidthArray.set(percentgeBandwidth.begin, percentgeBandwidth); + + } + + nextCandlesFile(); + + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> buildRecords -> loopBody -> buildPercentgeBandwidthArray -> err = " + err.message); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function nextCandlesFile() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildRecords -> loopBody -> nextCandlesFile -> Entering function."); } + + let fileName = market.assetA + '_' + market.assetB + ".json"; + + let filePathRoot = bot.devTeam + "/" + "AAOlivia" + "." + bot.version.major + "." + bot.version.minor + "/" + global.PLATFORM_CONFIG.codeName + "." + global.PLATFORM_CONFIG.version.major + "." + global.PLATFORM_CONFIG.version.minor + "/" + global.EXCHANGE_NAME + "/" + bot.dataSetVersion; + let filePath = filePathRoot + "/Output/" + CANDLES_FOLDER_NAME + "/" + "Multi-Period-Market" + "/" + timePeriod; + + oliviaStorage.getTextFile(filePath, fileName, onFileReceived, true); + + function onFileReceived(err, text) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildRecords -> loopBody -> nextCandlesFile -> onFileReceived -> Entering function."); } + if (LOG_FILE_CONTENT === true) { logger.write(MODULE_NAME, "[INFO] start -> buildRecords -> loopBody -> nextCandlesFile -> onFileReceived -> text = " + text); } + + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + + logger.write(MODULE_NAME, "[ERROR] start -> buildRecords -> loopBody -> nextCandlesFile -> onFileReceived -> err = " + err.message); + callBackFunction(err); + return; + + } + + marketFile = JSON.parse(text); + + buildCandles(); + + + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> buildRecords -> loopBody -> nextCandlesFile -> onFileReceived -> err = " + err.message); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> buildRecords -> loopBody -> nextCandlesFile -> err = " + err.message); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function buildCandles() { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildRecords -> loopBody -> buildCandles -> Entering function."); } + + try { + + for (let i = 0; i < marketFile.length; i++) { + + let candle = { + open: undefined, + close: undefined, + min: 10000000000000, + max: 0, + begin: undefined, + end: undefined, + direction: undefined + }; + + candle.min = marketFile[i][0]; + candle.max = marketFile[i][1]; + + candle.open = marketFile[i][2]; + candle.close = marketFile[i][3]; + + candle.begin = marketFile[i][4]; + candle.end = marketFile[i][5]; + + if (candle.open > candle.close) { candle.direction = 'down'; } + if (candle.open < candle.close) { candle.direction = 'up'; } + if (candle.open === candle.close) { candle.direction = 'side'; } + + candles.push(candle); + + } + + buildRecords(); + + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> buildRecords -> loopBody -> buildCandles -> err = " + err.message); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function buildRecords() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildRecords -> loopBody -> buildRecords -> Entering function."); } + + /* Building records */ + + let stopLossPercentage = 1; + let initialBalanceA = 1; + let record; + let lastOperation = 'Buy'; + let initialDate = new Date("2018-08-01"); + let balanceAssetA = initialBalanceA; + let balanceAssetB = 0; + let profit = 0; + let lastProfit = 0; + let stopLoss = 0; + let previousBalanceAssetA = 0; + let roundtrips = 0; + let hits = 0; + let fails = 0; + let hitRatio = 0; + let ROI = 0; + let periods = 0; + let days = 0; + let anualizedRateOfReturn = 0; + let type = ''; + let rate = 0; + let previousPBMovingAverage = 0; + let trailingStop = false; + + for (let i = 0; i < candles.length; i++) { + + let candle = candles[i]; + let percentgeBandwidth = percentgeBandwidthArray.get(candle.begin); + + if (percentgeBandwidth === undefined) { continue;} + + if (candle.begin < initialDate.valueOf()) { continue; } + + periods++; + + /* Sell condition */ + + if ( + percentgeBandwidth.value >= 80 && + lastOperation === 'Buy' && + (previousPBMovingAverage > percentgeBandwidth.movingAverage) + ) { + + previousBalanceAssetA = balanceAssetA; + lastProfit = 0; + + balanceAssetB = balanceAssetA * candle.close; + balanceAssetA = 0; + stopLoss = candle.close + candle.close * stopLossPercentage / 100; + rate = candle.close; + + type = '"Sell"'; + lastOperation = 'Sell'; + + addRecord(); + + continue; + }; + + /* Stop Loss condition */ + + let mustBuy = false; + + if (candle.max >= stopLoss && lastOperation === 'Sell') { + + balanceAssetA = balanceAssetB / stopLoss; + balanceAssetB = 0; + rate = stopLoss; + mustBuy = true; + } + + /* Buy condition */ + + if (percentgeBandwidth.value <= 50 && lastOperation === 'Sell' && mustBuy === false) { + + balanceAssetA = balanceAssetB / candle.close; + balanceAssetB = 0; + rate = candle.close; + mustBuy = true; + }; + + if (mustBuy === true) { + + stopLoss = 0; + trailingStop = false; + roundtrips++; + lastProfit = balanceAssetA - previousBalanceAssetA; + profit = profit + lastProfit; + ROI = (initialBalanceA + profit) / initialBalanceA - 1; + + if (lastProfit > 0) { + hits++; + } else { + fails++; + } + hitRatio = hits / roundtrips; + + let miliSecondsPerDay = 24 * 60 * 60 * 1000; + days = periods * outputPeriod / miliSecondsPerDay; + anualizedRateOfReturn = ROI / days * 365; + + type = '"Buy"'; + lastOperation = 'Buy'; + + addRecord(); + + continue; + + } + + /* Not a buy or sell condition */ + + type = '""'; + addRecord(); + previousPBMovingAverage = percentgeBandwidth.movingAverage; + + function addRecord() { + + record = { + begin: candle.begin, + end: candle.end, + type: type, + rate: rate, + amount: 1, + balanceA: balanceAssetA, + balanceB: balanceAssetB, + profit: profit, + lastProfit: lastProfit, + stopLoss: stopLoss, + roundtrips: roundtrips, + hits: hits, + fails: fails, + hitRatio: hitRatio, + ROI: ROI, + periods: periods, + days: days, + anualizedRateOfReturn: anualizedRateOfReturn + } + + recordsArray.push(record); + } + } + + writeRecordsFile(); + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> buildRecords -> loopBody -> buildRecords -> err = " + err.message); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function writeRecordsFile() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildRecords -> loopBody -> writeRecordsFile -> Entering function."); } + + let separator = ""; + let fileRecordCounter = 0; + + let fileContent = ""; + + for (i = 0; i < recordsArray.length; i++) { + + let record = recordsArray[i]; + + fileContent = fileContent + separator + '[' + + record.begin + "," + + record.end + "," + + record.type + "," + + record.rate + "," + + record.amount + "," + + record.balanceA + "," + + record.balanceB + "," + + record.profit + "," + + record.lastProfit + "," + + record.stopLoss + "," + + record.roundtrips + "," + + record.hits + "," + + record.fails + "," + + record.hitRatio + "," + + record.ROI + "," + + record.periods + "," + + record.days + "," + + record.anualizedRateOfReturn + "]"; + + if (separator === "") { separator = ","; } + + fileRecordCounter++; + + } + + fileContent = "[" + fileContent + "]"; + + let fileName = '' + market.assetA + '_' + market.assetB + '.json'; + + let filePathRoot = bot.devTeam + "/" + bot.codeName + "." + bot.version.major + "." + bot.version.minor + "/" + global.PLATFORM_CONFIG.codeName + "." + global.PLATFORM_CONFIG.version.major + "." + global.PLATFORM_CONFIG.version.minor + "/" + global.EXCHANGE_NAME + "/" + bot.dataSetVersion; + let filePath = filePathRoot + "/Output/" + SIMULATED_RECORDS_FOLDER_NAME + "/" + "Multi-Period-Market" + "/" + timePeriod; + + jasonStorage.createTextFile(filePath, fileName, fileContent + '\n', onFileCreated); + + function onFileCreated(err) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildRecords -> loopBody -> writeRecordsFile -> onFileCreated -> Entering function."); } + if (LOG_FILE_CONTENT === true) { logger.write(MODULE_NAME, "[INFO] start -> buildRecords -> loopBody -> writeRecordsFile -> onFileCreated -> fileContent = " + fileContent); } + + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + + logger.write(MODULE_NAME, "[ERROR] start -> buildRecords -> loopBody -> writeRecordsFile -> onFileCreated -> err = " + err.message); + logger.write(MODULE_NAME, "[ERROR] start -> buildRecords -> loopBody -> writeRecordsFile -> onFileCreated -> filePath = " + filePath); + logger.write(MODULE_NAME, "[ERROR] start -> buildRecords -> loopBody -> writeRecordsFile -> onFileCreated -> market = " + market.assetA + "_" + market.assetB); + + callBackFunction(err); + return; + + } + + controlLoop(); + + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> buildRecords -> loopBody -> writeRecordsFile -> onFileCreated -> err = " + err.message); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> buildRecords -> loopBody -> writeRecordsFile -> err = " + err.message); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> buildRecords -> loopBody -> err = " + err.message); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function controlLoop() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildRecords -> controlLoop -> Entering function."); } + + n++; + + if (n < global.marketFilesPeriods.length) { + + loopBody(); + + } else { + + writeStatusReport(callBackFunction); + + } + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> buildRecords -> controlLoop -> err = " + err.message); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> buildRecords -> err = " + err.message); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function writeStatusReport(callBack) { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeStatusReport -> Entering function."); } + + try { + + let reportKey = "AAMasters" + "-" + "AAJason" + "-" + "Multi-Period-Market" + "-" + "dataSet.V1"; + let thisReport = statusDependencies.statusReports.get(reportKey); + + thisReport.file.lastExecution = bot.processDatetime; + thisReport.save(callBack); + + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> writeStatusReport -> err = " + err.message); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> err = " + err.message); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } +}; diff --git a/Project.njsproj b/Project.njsproj new file mode 100644 index 000000000..4cab6c556 --- /dev/null +++ b/Project.njsproj @@ -0,0 +1,91 @@ + + + + Debug + 2.0 + {aa2901bc-8f81-40f0-a402-5ca79a3f22ad} + + ShowAllFiles + + + c:\users\luis\source\repos\AAOlivia\Multi-Period-Market + . + {3AF33F2E-1136-4D97-BBB7-1795711AC8B8};{349c5851-65df-11da-9384-00065b846f21};{9092AA53-FB77-4645-B42D-1CCCA6BD08BD} + 14.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + Project + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + False + True + 0 + / + http://localhost:48022/ + False + True + http://localhost:1337 + False + + + + + + + CurrentPage + True + False + False + False + + + + + + + + + False + False + + + + + \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 000000000..099b74e58 --- /dev/null +++ b/README.md @@ -0,0 +1,7 @@ +# I'm Jason, an AA i-Bot! + +# Disclaimer + +THE AA MASTERS BOTS AND THEIR ASSOCIATED PRODUCTS AND SERVICES ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, SUITABILITY FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. + +IN NO EVENT WILL ADVANCED ALGOS BE LIABLE TO ANY PARTY FOR ANY DIRECT, INDIRECT, SPECIAL OR OTHER CONSEQUENTIAL DAMAGES FOR ANY USE OF THE AACLOUD, THE AA ARENA COMPETITION, THE AA MASTERS BOTS, OR ANY OTHER ASSOCIATED SERVICE OR WEB SITE, INCLUDING, WITHOUT LIMITATION, ANY LOST PROFITS, BUSINESS INTERRUPTION, LOSS OF FUNDS, PROGRAMS OR OTHER DATA OR OTHERWISE, EVEN IF WE ARE EXPRESSLY ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. diff --git a/package.json b/package.json new file mode 100644 index 000000000..11cc6a7f1 --- /dev/null +++ b/package.json @@ -0,0 +1,13 @@ +{ + "name": "AAToma", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "Luis-Fernando-Molina", + "license": "ISC", + "dependencies": { + } +} diff --git a/this.bot.config.json b/this.bot.config.json new file mode 100644 index 000000000..e4aeadbf9 --- /dev/null +++ b/this.bot.config.json @@ -0,0 +1,154 @@ +{ + "displayName": "Jason", + "codeName": "AAJason", + "type": "Indicator", + "version": { + "major": 1, + "minor": 0, + "patch": 0 + }, + "devTeam": "AAMasters", + "profilePicture": "Jason.png", + "dataSetVersion": "dataSet.V1", + "processes": [ + { + "name": "Multi-Period-Market", + "description": "Produces a single file for each time period covering all the market.", + "startMode": { + "allMonths": { + "run": "false", + "minYear": "", + "maxYear": "" + }, + "oneMonth": { + "run": "false", + "year": "", + "month": "" + }, + "noTime": { + "run": "true" + }, + "fixedInterval": { + "run": "false", + "interval": 0 + } + }, + "normalWaitTime": 60000, + "retryWaitTime": 10000, + "sleepWaitTime": 3600000, + "comaWaitTime": 86400000, + "statusDependencies": [ + { + "devTeam": "AAMasters", + "bot": "AAJason", + "botVersion": { + "major": 1, + "minor": 0 + }, + "process": "Multi-Period-Market", + "dataSetVersion": "dataSet.V1" + } + ] + }, + { + "name": "Multi-Period-Daily", + "description": "Produces a Daily File at each time period.", + "startMode": { + "allMonths": { + "run": "false", + "minYear": "", + "maxYear": "" + }, + "oneMonth": { + "run": "false", + "year": "", + "month": "" + }, + "noTime": { + "run": "true" + }, + "fixedInterval": { + "run": "false", + "interval": 0 + } + }, + "normalWaitTime": 60000, + "retryWaitTime": 10000, + "sleepWaitTime": 3600000, + "comaWaitTime": 86400000, + "statusDependencies": [ + { + "devTeam": "AAMasters", + "bot": "AACharly", + "botVersion": { + "major": 1, + "minor": 0 + }, + "process": "Poloniex-Historic-Records", + "dataSetVersion": "dataSet.V1" + }, + { + "devTeam": "AAMasters", + "bot": "AAChris", + "botVersion": { + "major": 1, + "minor": 0 + }, + "process": "Multi-Period-Daily", + "dataSetVersion": "dataSet.V1" + }, + { + "devTeam": "AAMasters", + "bot": "AAJason", + "botVersion": { + "major": 1, + "minor": 0 + }, + "process": "Multi-Period-Daily", + "dataSetVersion": "dataSet.V1" + } + ] + } + ], + "products": [ + { + "codeName": "Trading-Simulation", + "displayName": "Trading Simulation", + "description": "Trading Simulation", + "storageAccount": "aajason", + "dataSets": [ + { + "codeName": "Multi-Period-Market", + "type": "Market Files", + "validPeriods": [ "24-hs", "12-hs", "08-hs", "06-hs", "04-hs", "03-hs", "02-hs", "01-hs" ], + "filePath": "AAMasters/AAJason.1.0/AACloud.1.1/@Exchange/dataSet.V1/Output/Trading-Simulation/Multi-Period-Market/@Period", + "fileName": "@AssetA_@AssetB.json" + }, + { + "codeName": "Multi-Period-Daily", + "type": "Daily Files", + "validPeriods": [ "45-min", "40-min", "30-min", "20-min", "15-min", "10-min", "05-min", "04-min", "03-min", "02-min", "01-min" ], + "filePath": "AAMasters/AAJason.1.0/AACloud.1.1/@Exchange/dataSet.V1/Output/Trading-Simulation/Multi-Period-Daily/@Period/@Year/@Month/@Day", + "fileName": "@AssetA_@AssetB.json", + "dataRange": { + "filePath": "AAMasters/AAJason.1.0/AACloud.1.1/@Exchange/dataSet.V1/Output/Trading-Simulation/Multi-Period-Daily", + "fileName": "Data.Range.@AssetA_@AssetB.json" + } + } + ], + "exchangeList": [ + { + "name": "Poloniex" + } + ], + "plotter": { + "devTeam": "AAMasters", + "codeName": "PlottersTradingSimulation", + "moduleName": "TradingSimulation" + } + } + ] +} + + +