diff --git a/Commons.js b/Commons.js index 779995fe5..191e0905b 100644 --- a/Commons.js +++ b/Commons.js @@ -56,6 +56,8 @@ exports.newCommons = function newCommons(bot, logger, UTILITIES) { async function runSimulation( recordsArray, conditionsArray, + strategiesArray, + tradesArray, lastObjectsArray, simulationLogic, timePeriod, @@ -114,6 +116,20 @@ exports.newCommons = function newCommons(bot, logger, UTILITIES) { let rate = 0; let newStopLoss; + let currentStrategy = { + begin: 0, + end: 0, + status: 0, + number: 0 + } + + let currentTrade = { + begin: 0, + end: 0, + status: 0, + profit: 0 + } + /* The following counters need to survive multiple executions of the similator and keep themselves reliable. This is challenging when the simulation is executed using Daily Files, since each execution means a new @@ -397,7 +413,8 @@ exports.newCommons = function newCommons(bot, logger, UTILITIES) { } } - /* While we are outside all strategies, we evaluate whether it is time to enter one or not. */ + + /* Strategy Enter Condition */ if (strategyNumber === 0 && balanceAssetA > minimunBalanceA) { @@ -440,7 +457,9 @@ exports.newCommons = function newCommons(bot, logger, UTILITIES) { strategyPhase = 1; strategyNumber = j + 1; - + currentStrategy.begin = candle.begin; + currentStrategy.beginRate = candle.min; + currentStrategy.endRate = candle.min; // In case the strategy does not get exited return; } } @@ -475,6 +494,10 @@ exports.newCommons = function newCommons(bot, logger, UTILITIES) { if (passed) { + currentStrategy.number = strategyNumber - 1 + currentStrategy.end = candle.end; + currentStrategy.endRate = candle.min; + currentStrategy.status = 1; strategyPhase = 0; strategyNumber = 0; @@ -484,6 +507,8 @@ exports.newCommons = function newCommons(bot, logger, UTILITIES) { } } + /* Checking if Stop or Take Profit were hit */ + if (strategyPhase === 3) { /* Checking what happened since the last execution. We need to know if the Stop Loss @@ -506,6 +531,13 @@ exports.newCommons = function newCommons(bot, logger, UTILITIES) { rate = stopLoss; type = '"Buy@StopLoss"'; strategyPhase = 4; + currentTrade.end = candle.end; + currentTrade.status = 1; + + currentStrategy.number = strategyNumber - 1 + currentStrategy.end = candle.end; + currentStrategy.endRate = candle.min; + currentStrategy.status = 1; } /* Buy Order condition: Here we verify if the Buy Order was filled or not. */ @@ -526,10 +558,17 @@ exports.newCommons = function newCommons(bot, logger, UTILITIES) { rate = buyOrder; type = '"Buy@BuyOrder"'; strategyPhase = 4; + currentTrade.end = candle.end; + currentTrade.status = 1; + + currentStrategy.number = strategyNumber - 1 + currentStrategy.end = candle.end; + currentStrategy.endRate = candle.min; + currentStrategy.status = 1; } } - /* Strategy Sell Condition */ + /* Trade Enter Condition */ if (strategyPhase === 1) { @@ -561,7 +600,7 @@ exports.newCommons = function newCommons(bot, logger, UTILITIES) { strategyPhase = 2; stopLossPhase = 1; buyOrderPhase = 1; - + currentTrade.begin = candle.begin; return; } } @@ -619,7 +658,7 @@ exports.newCommons = function newCommons(bot, logger, UTILITIES) { } } - /* Buy Order Management */ + /* Take Profit Management */ if (strategyPhase === 3) { @@ -664,7 +703,7 @@ exports.newCommons = function newCommons(bot, logger, UTILITIES) { } } - /* Check if we need to sell. */ + /* Entering into a Trade */ if (strategyPhase === 2) { @@ -698,7 +737,7 @@ exports.newCommons = function newCommons(bot, logger, UTILITIES) { continue; } - /* Check if we need to buy. */ + /* Exiting a Trade */ if (strategyPhase === 4) { @@ -768,7 +807,6 @@ exports.newCommons = function newCommons(bot, logger, UTILITIES) { strategyPhase = 0; stopLossPhase = 0; buyOrderPhase = 0; - continue; } @@ -821,7 +859,8 @@ exports.newCommons = function newCommons(bot, logger, UTILITIES) { 0, "") - } else { + } + else { orderRecord = createMessage( messageId, @@ -882,6 +921,8 @@ exports.newCommons = function newCommons(bot, logger, UTILITIES) { type = '""'; + /* Prepare the information for the Conditions File */ + conditionsArrayRecord.push(strategyNumber); conditionsArrayRecord.push(strategyPhase); conditionsArrayRecord.push(stopLossPhase); @@ -890,6 +931,43 @@ exports.newCommons = function newCommons(bot, logger, UTILITIES) { conditionsArray.push(conditionsArrayRecord); + /* Prepare the information for the Strategies File*/ + + if ( + (currentStrategy.begin !== 0 && currentStrategy.end !== 0) || + (currentStrategy.begin !== 0 && i === candles.length - 1) + ) { + + strategiesArray.push(currentStrategy); + + currentStrategy = { + begin: 0, + end: 0, + status: 0, + number: 0, + beginRate: 0, + endRate: 0 + } + } + + /* Prepare the information for the Trades File */ + + if ( + (currentTrade.begin !== 0 && currentTrade.end !== 0) || + (currentTrade.begin !== 0 && i === candles.length - 1) + ) { + + currentTrade.profit = lastProfit; + + tradesArray.push(currentTrade); + + currentTrade = { + begin: 0, + end: 0, + status: 0, + profit: 0 + } + } } } diff --git a/Multi-Period-Daily/User.Bot.js b/Multi-Period-Daily/User.Bot.js index b155229ca..29a9c2552 100644 --- a/Multi-Period-Daily/User.Bot.js +++ b/Multi-Period-Daily/User.Bot.js @@ -7,6 +7,8 @@ const SIMULATED_RECORDS_FOLDER_NAME = "Trading-Simulation"; const CONDITIONS_FOLDER_NAME = "Simulation-Conditions"; + const STRATEGIES_FOLDER_NAME = "Simulation-Strategies"; + const TRADES_FOLDER_NAME = "Simulation-Trades"; const commons = COMMONS.newCommons(bot, logger, UTILITIES); @@ -63,6 +65,8 @@ let recordsArray = []; let conditionsArray = []; + let strategiesArray = []; + let tradesArray = []; let simulationLogic = {}; let lastObjectsArray = []; @@ -106,6 +110,8 @@ commons.runSimulation( recordsArray, conditionsArray, + strategiesArray, + tradesArray, lastObjectsArray, simulationLogic, timePeriod, @@ -226,6 +232,10 @@ let record = conditionsArray[i]; + /* Will only add to the file the records of the current day */ + + if (record.begin < currentDay.valueOf()) { continue; } + let conditions = ""; let conditionsSeparator = ""; @@ -277,7 +287,7 @@ } - callBackFunction(global.DEFAULT_OK_RESPONSE); + writeStrategiesFile(); } catch (err) { @@ -292,6 +302,156 @@ } } + function writeStrategiesFile() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeStrategiesFile -> Entering function."); } + + let separator = ""; + let fileRecordCounter = 0; + + let fileContent = ""; + + for (let i = 0; i < strategiesArray.length; i++) { + let record = strategiesArray[i]; + + /* Will only add to the file the records of the current day */ + + if (record.begin < currentDay.valueOf()) { continue; } + + fileContent = fileContent + separator + '[' + + record.begin + "," + + record.end + "," + + record.status + "," + + record.number + "," + + record.beginRate + "," + + record.endRate + "]"; + + if (separator === "") { separator = ","; } + + fileRecordCounter++; + + } + + fileContent = "[" + fileContent + "]"; + + let dateForPath = currentDay.getUTCFullYear() + '/' + utilities.pad(currentDay.getUTCMonth() + 1, 2) + '/' + utilities.pad(currentDay.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/" + STRATEGIES_FOLDER_NAME + "/" + "Multi-Period-Daily" + "/" + outputPeriodLabel + "/" + dateForPath; + + thisBotStorage.createTextFile(filePath, fileName, fileContent + '\n', onFileCreated); + + function onFileCreated(err) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeStrategiesFile -> onFileCreated -> Entering function."); } + if (LOG_FILE_CONTENT === true) { logger.write(MODULE_NAME, "[INFO] start -> writeStrategiesFile -> onFileCreated -> fileContent = " + fileContent); } + + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + + logger.write(MODULE_NAME, "[ERROR] start -> writeStrategiesFile -> onFileCreated -> err = " + err.message); + logger.write(MODULE_NAME, "[ERROR] start -> writeStrategiesFile -> onFileCreated -> filePath = " + filePath); + logger.write(MODULE_NAME, "[ERROR] start -> writeStrategiesFile -> onFileCreated -> market = " + market.assetA + "_" + market.assetB); + + callBackFunction(err); + return; + + } + + writeTradesFile(); + + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> writeStrategiesFile -> onFileCreated -> err = " + err.message); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> writeStrategiesFile -> err = " + err.message); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function writeTradesFile() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeTradesFile -> Entering function."); } + + let separator = ""; + let fileRecordCounter = 0; + + let fileContent = ""; + + for (let i = 0; i < tradesArray.length; i++) { + + let record = tradesArray[i]; + + /* Will only add to the file the records of the current day */ + + if (record.begin < currentDay.valueOf()) { continue; } + + fileContent = fileContent + separator + '[' + + record.begin + "," + + record.end + "," + + record.status + "," + + record.profit + "]"; + + if (separator === "") { separator = ","; } + + fileRecordCounter++; + + } + + fileContent = "[" + fileContent + "]"; + + let dateForPath = currentDay.getUTCFullYear() + '/' + utilities.pad(currentDay.getUTCMonth() + 1, 2) + '/' + utilities.pad(currentDay.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/" + TRADES_FOLDER_NAME + "/" + "Multi-Period-Daily" + "/" + outputPeriodLabel + "/" + dateForPath; + + thisBotStorage.createTextFile(filePath, fileName, fileContent + '\n', onFileCreated); + + function onFileCreated(err) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeTradesFile -> onFileCreated -> Entering function."); } + if (LOG_FILE_CONTENT === true) { logger.write(MODULE_NAME, "[INFO] start -> writeTradesFile -> onFileCreated -> fileContent = " + fileContent); } + + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + + logger.write(MODULE_NAME, "[ERROR] start -> writeTradesFile -> onFileCreated -> err = " + err.message); + logger.write(MODULE_NAME, "[ERROR] start -> writeTradesFile -> onFileCreated -> filePath = " + filePath); + logger.write(MODULE_NAME, "[ERROR] start -> writeTradesFile -> onFileCreated -> market = " + market.assetA + "_" + market.assetB); + + callBackFunction(err); + return; + + } + + callBackFunction(global.DEFAULT_OK_RESPONSE); + + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> writeTradesFile -> onFileCreated -> err = " + err.message); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> writeTradesFile -> err = " + err.message); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + } catch (err) { logger.write(MODULE_NAME, "[ERROR] start -> err = " + err.message); diff --git a/Multi-Period-Market/User.Bot.js b/Multi-Period-Market/User.Bot.js index c9090cc63..acc354cdd 100644 --- a/Multi-Period-Market/User.Bot.js +++ b/Multi-Period-Market/User.Bot.js @@ -7,6 +7,8 @@ const SIMULATED_RECORDS_FOLDER_NAME = "Trading-Simulation"; const CONDITIONS_FOLDER_NAME = "Simulation-Conditions"; + const STRATEGIES_FOLDER_NAME = "Simulation-Strategies"; + const TRADES_FOLDER_NAME = "Simulation-Trades"; const commons = COMMONS.newCommons(bot, logger, UTILITIES); @@ -63,6 +65,8 @@ let recordsArray = []; let conditionsArray = []; + let strategiesArray = []; + let tradesArray = []; let lastObjectsArray = []; let simulationLogic = {}; @@ -108,6 +112,8 @@ commons.runSimulation( recordsArray, conditionsArray, + strategiesArray, + tradesArray, lastObjectsArray, simulationLogic, timePeriod, @@ -268,7 +274,7 @@ } - callBackFunction(global.DEFAULT_OK_RESPONSE); + writeStrategiesFile(); } catch (err) { @@ -283,6 +289,143 @@ } } + function writeStrategiesFile() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeStrategiesFile -> Entering function."); } + + let separator = ""; + let fileRecordCounter = 0; + + let fileContent = ""; + + for (let i = 0; i < strategiesArray.length; i++) { + let record = strategiesArray[i]; + + fileContent = fileContent + separator + '[' + + record.begin + "," + + record.end + "," + + record.status + "," + + record.number + "," + + record.beginRate + "," + + record.endRate + "]"; + + 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/" + STRATEGIES_FOLDER_NAME + "/" + "Multi-Period-Market" + "/" + outputPeriodLabel; + + thisBotStorage.createTextFile(filePath, fileName, fileContent + '\n', onFileCreated); + + function onFileCreated(err) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeStrategiesFile -> onFileCreated -> Entering function."); } + if (LOG_FILE_CONTENT === true) { logger.write(MODULE_NAME, "[INFO] start -> writeStrategiesFile -> onFileCreated -> fileContent = " + fileContent); } + + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + + logger.write(MODULE_NAME, "[ERROR] start -> writeStrategiesFile -> onFileCreated -> err = " + err.message); + logger.write(MODULE_NAME, "[ERROR] start -> writeStrategiesFile -> onFileCreated -> filePath = " + filePath); + logger.write(MODULE_NAME, "[ERROR] start -> writeStrategiesFile -> onFileCreated -> market = " + market.assetA + "_" + market.assetB); + + callBackFunction(err); + return; + + } + + writeTradesFile(); + + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> writeStrategiesFile -> onFileCreated -> err = " + err.message); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> writeStrategiesFile -> err = " + err.message); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function writeTradesFile() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeTradesFile -> Entering function."); } + + let separator = ""; + let fileRecordCounter = 0; + + let fileContent = ""; + + for (let i = 0; i < tradesArray.length; i++) { + + let record = tradesArray[i]; + + fileContent = fileContent + separator + '[' + + record.begin + "," + + record.end + "," + + record.status + "," + + record.profit + "]"; + + 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/" + TRADES_FOLDER_NAME + "/" + "Multi-Period-Market" + "/" + outputPeriodLabel; + + thisBotStorage.createTextFile(filePath, fileName, fileContent + '\n', onFileCreated); + + function onFileCreated(err) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeTradesFile -> onFileCreated -> Entering function."); } + if (LOG_FILE_CONTENT === true) { logger.write(MODULE_NAME, "[INFO] start -> writeTradesFile -> onFileCreated -> fileContent = " + fileContent); } + + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + + logger.write(MODULE_NAME, "[ERROR] start -> writeTradesFile -> onFileCreated -> err = " + err.message); + logger.write(MODULE_NAME, "[ERROR] start -> writeTradesFile -> onFileCreated -> filePath = " + filePath); + logger.write(MODULE_NAME, "[ERROR] start -> writeTradesFile -> onFileCreated -> market = " + market.assetA + "_" + market.assetB); + + callBackFunction(err); + return; + + } + + callBackFunction(global.DEFAULT_OK_RESPONSE); + + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> writeTradesFile -> onFileCreated -> err = " + err.message); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> writeTradesFile -> err = " + err.message); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + } catch (err) { logger.write(MODULE_NAME, "[ERROR] start -> err = " + err.message); diff --git a/this.bot.config.json b/this.bot.config.json index 9b033db43..1bf3db7cc 100644 --- a/this.bot.config.json +++ b/this.bot.config.json @@ -281,7 +281,7 @@ { "codeName": "Multi-Period-Daily", "type": "Daily Files", - "validPeriods": ["15-min", "10-min", "05-min"], + "validPeriods": [ "15-min", "10-min", "05-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": { @@ -320,11 +320,11 @@ { "codeName": "Multi-Period-Daily", "type": "Daily Files", - "validPeriods": ["15-min", "10-min", "05-min"], + "validPeriods": [ "15-min", "10-min", "05-min" ], "filePath": "AAMasters/AAJason.1.0/AACloud.1.1/@Exchange/dataSet.V1/Output/Simulation-Conditions/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", + "filePath": "AAMasters/AAJason.1.0/AACloud.1.1/@Exchange/dataSet.V1/Output/Simulation-Conditions/Multi-Period-Daily", "fileName": "Data.Range.@AssetA_@AssetB.json" } } @@ -342,6 +342,84 @@ "codeName": "PlottersTradingSimulation", "moduleName": "Conditions" } + }, + { + "codeName": "Simulation-Strategies", + "displayName": "Simulation Strategies", + "description": "Simulation Strategies", + "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/Simulation-Strategies/Multi-Period-Market/@Period", + "fileName": "@AssetA_@AssetB.json" + }, + { + "codeName": "Multi-Period-Daily", + "type": "Daily Files", + "validPeriods": [ "15-min", "10-min", "05-min" ], + "filePath": "AAMasters/AAJason.1.0/AACloud.1.1/@Exchange/dataSet.V1/Output/Simulation-Strategies/Multi-Period-Daily/@Period/@Year/@Month/@Day", + "fileName": "@AssetA_@AssetB.json", + "dataRange": { + "filePath": "AAMasters/AAJason.1.0/AACloud.1.1/@Exchange/dataSet.V1/Output/Simulation-Strategies/Multi-Period-Daily", + "fileName": "Data.Range.@AssetA_@AssetB.json" + } + } + ], + "exchangeList": [ + { + "name": "Poloniex" + }, + { + "name": "Coss" + } + ], + "plotter": { + "devTeam": "AAMasters", + "codeName": "PlottersTradingSimulation", + "moduleName": "Strategies" + } + }, + { + "codeName": "Simulation-Trades", + "displayName": "Simulation Trades", + "description": "Simulation Trades", + "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/Simulation-Trades/Multi-Period-Market/@Period", + "fileName": "@AssetA_@AssetB.json" + }, + { + "codeName": "Multi-Period-Daily", + "type": "Daily Files", + "validPeriods": [ "15-min", "10-min", "05-min" ], + "filePath": "AAMasters/AAJason.1.0/AACloud.1.1/@Exchange/dataSet.V1/Output/Simulation-Trades/Multi-Period-Daily/@Period/@Year/@Month/@Day", + "fileName": "@AssetA_@AssetB.json", + "dataRange": { + "filePath": "AAMasters/AAJason.1.0/AACloud.1.1/@Exchange/dataSet.V1/Output/Simulation-Trades/Multi-Period-Daily", + "fileName": "Data.Range.@AssetA_@AssetB.json" + } + } + ], + "exchangeList": [ + { + "name": "Poloniex" + }, + { + "name": "Coss" + } + ], + "plotter": { + "devTeam": "AAMasters", + "codeName": "PlottersTradingSimulation", + "moduleName": "Trades" + } } ] }