519 lines
23 KiB
JavaScript
519 lines
23 KiB
JavaScript
exports.newUserBot = function newUserBot(bot, logger, COMMONS, UTILITIES, fileStorage) {
|
|
|
|
const FULL_LOG = true;
|
|
const LOG_FILE_CONTENT = false;
|
|
|
|
const MODULE_NAME = "User Bot";
|
|
|
|
const EXECUTION_FOLDER_NAME = "Trading-Execution";
|
|
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);
|
|
|
|
thisObject = {
|
|
initialize: initialize,
|
|
start: start
|
|
};
|
|
|
|
let dataDependencies;
|
|
|
|
return thisObject;
|
|
|
|
function initialize(pDataDependencies, callBackFunction) {
|
|
|
|
try {
|
|
|
|
logger.fileName = MODULE_NAME;
|
|
logger.initialize();
|
|
|
|
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] initialize -> Entering function."); }
|
|
|
|
dataDependencies = pDataDependencies;
|
|
|
|
callBackFunction(global.DEFAULT_OK_RESPONSE);
|
|
|
|
} catch (err) {
|
|
logger.write(MODULE_NAME, "[ERROR] initialize -> err = " + err.stack);
|
|
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
|
|
}
|
|
}
|
|
|
|
function start(dataFiles, timePeriod, outputPeriodLabel, startDate, endDate, callBackFunction) {
|
|
|
|
try {
|
|
|
|
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> Entering function."); }
|
|
|
|
let market = global.MARKET;
|
|
let dataFile;
|
|
|
|
let recordsArray
|
|
let conditionsArray
|
|
let strategiesArray
|
|
let tradesArray
|
|
let lastObjectsArray
|
|
|
|
let tradingSystem = {};
|
|
let interExecutionMemory = {};
|
|
let currentDay = bot.processDatetime;
|
|
|
|
commons.initializeData();
|
|
|
|
for (let i = 0; i < dataDependencies.config.length; i++) {
|
|
|
|
let dependency = dataDependencies.config[i];
|
|
dataFile = dataFiles[i];
|
|
|
|
switch (i) {
|
|
|
|
case 0: {
|
|
commons.buildLRC(dataFile, callBackFunction);
|
|
break;
|
|
}
|
|
case 1: {
|
|
commons.buildPercentageBandwidthMap(dataFile, callBackFunction);
|
|
break;
|
|
}
|
|
case 2: {
|
|
commons.buildBollingerBandsMap(dataFile, callBackFunction);
|
|
break;
|
|
}
|
|
case 3: {
|
|
commons.buildBollingerChannelsArray(dataFile, callBackFunction);
|
|
break;
|
|
}
|
|
case 4: {
|
|
commons.buildBollingerSubChannelsArray(dataFile, callBackFunction);
|
|
break;
|
|
}
|
|
case 5: {
|
|
commons.buildCandles(dataFile, callBackFunction);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
commons.runSimulation(
|
|
timePeriod,
|
|
currentDay,
|
|
startDate,
|
|
endDate,
|
|
interExecutionMemory,
|
|
writeFiles,
|
|
callBackFunction)
|
|
|
|
function writeFiles(pTradingSystem, pExecutionArray, pRecordsArray, pConditionsArray, pStrategiesArray, pTradesArray, pLastObjectsArray) {
|
|
|
|
tradingSystem = pTradingSystem
|
|
executionArray = pExecutionArray
|
|
recordsArray = pRecordsArray
|
|
conditionsArray = pConditionsArray
|
|
strategiesArray = pStrategiesArray
|
|
tradesArray = pTradesArray
|
|
lastObjectsArray = pLastObjectsArray
|
|
|
|
writeExecutionFile()
|
|
}
|
|
|
|
function writeExecutionFile() {
|
|
|
|
try {
|
|
|
|
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeExecutionFile -> Entering function."); }
|
|
|
|
let separator = "";
|
|
let fileRecordCounter = 0;
|
|
|
|
let fileContent = "";
|
|
|
|
for (let i = 0; i < executionArray.length; i++) {
|
|
|
|
let record = executionArray[i];
|
|
|
|
fileContent = fileContent + separator + '[' +
|
|
record.begin + "," +
|
|
record.end + "," +
|
|
JSON.stringify(record.executionRecord) + "]";
|
|
|
|
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.CLONE_EXECUTOR.codeName + "." + global.CLONE_EXECUTOR.version + "/" + global.EXCHANGE_NAME + "/" + bot.dataSetVersion;
|
|
let filePath = filePathRoot + "/Output/" + EXECUTION_FOLDER_NAME + "/" + "Multi-Period-Market" + "/" + outputPeriodLabel;
|
|
filePath += '/' + fileName
|
|
|
|
fileStorage.createTextFile(bot.devTeam, filePath, fileContent + '\n', onFileCreated);
|
|
|
|
function onFileCreated(err) {
|
|
|
|
try {
|
|
|
|
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeExecutionFile -> onFileCreated -> Entering function."); }
|
|
if (LOG_FILE_CONTENT === true) { logger.write(MODULE_NAME, "[INFO] start -> writeExecutionFile -> onFileCreated -> fileContent = " + fileContent); }
|
|
|
|
if (err.result !== global.DEFAULT_OK_RESPONSE.result) {
|
|
|
|
logger.write(MODULE_NAME, "[ERROR] start -> writeExecutionFile -> onFileCreated -> err = " + err.stack);
|
|
logger.write(MODULE_NAME, "[ERROR] start -> writeExecutionFile -> onFileCreated -> filePath = " + filePath);
|
|
logger.write(MODULE_NAME, "[ERROR] start -> writeExecutionFile -> onFileCreated -> market = " + market.assetA + "_" + market.assetB);
|
|
|
|
callBackFunction(err);
|
|
return;
|
|
|
|
}
|
|
|
|
writeRecordsFile();
|
|
|
|
}
|
|
catch (err) {
|
|
logger.write(MODULE_NAME, "[ERROR] start -> writeExecutionFile -> onFileCreated -> err = " + err.stack);
|
|
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
|
|
}
|
|
}
|
|
}
|
|
catch (err) {
|
|
logger.write(MODULE_NAME, "[ERROR] start -> writeExecutionFile -> err = " + err.stack);
|
|
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
|
|
}
|
|
}
|
|
|
|
function writeRecordsFile() {
|
|
|
|
try {
|
|
|
|
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeRecordsFile -> Entering function."); }
|
|
|
|
let separator = "";
|
|
let fileRecordCounter = 0;
|
|
|
|
let fileContent = "";
|
|
|
|
for (let i = 0; i < recordsArray.length; i++) {
|
|
|
|
let record = recordsArray[i];
|
|
|
|
fileContent = fileContent + separator + '[' +
|
|
record.begin + "," +
|
|
record.end + "," +
|
|
record.type + "," +
|
|
record.marketRate + "," +
|
|
record.amount + "," +
|
|
record.balanceA + "," +
|
|
record.balanceB + "," +
|
|
record.profit + "," +
|
|
record.lastTradeProfitLoss + "," +
|
|
record.stopLoss + "," +
|
|
record.roundtrips + "," +
|
|
record.hits + "," +
|
|
record.fails + "," +
|
|
record.hitRatio + "," +
|
|
record.ROI + "," +
|
|
record.periods + "," +
|
|
record.days + "," +
|
|
record.anualizedRateOfReturn + "," +
|
|
record.positionRate + "," +
|
|
record.lastTradeROI + "," +
|
|
record.strategy + "," +
|
|
record.strategyStageNumber + "," +
|
|
record.takeProfit + "," +
|
|
record.stopLossPhase + "," +
|
|
record.takeProfitPhase + "," +
|
|
JSON.stringify(record.executionRecord) + "," +
|
|
record.positionSize + "," +
|
|
record.initialBalanceA + "," +
|
|
record.minimumBalanceA + "," +
|
|
record.maximumBalanceA + "," +
|
|
record.initialBalanceB + "," +
|
|
record.minimumBalanceB + "," +
|
|
record.maximumBalanceB + "," +
|
|
record.baseAsset + "]";
|
|
|
|
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.CLONE_EXECUTOR.codeName + "." + global.CLONE_EXECUTOR.version + "/" + global.EXCHANGE_NAME + "/" + bot.dataSetVersion;
|
|
let filePath = filePathRoot + "/Output/" + SIMULATED_RECORDS_FOLDER_NAME + "/" + "Multi-Period-Market" + "/" + outputPeriodLabel;
|
|
filePath += '/' + fileName
|
|
|
|
fileStorage.createTextFile(bot.devTeam, filePath, fileContent + '\n', onFileCreated);
|
|
|
|
function onFileCreated(err) {
|
|
|
|
try {
|
|
|
|
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeRecordsFile -> onFileCreated -> Entering function."); }
|
|
if (LOG_FILE_CONTENT === true) { logger.write(MODULE_NAME, "[INFO] start -> writeRecordsFile -> onFileCreated -> fileContent = " + fileContent); }
|
|
|
|
if (err.result !== global.DEFAULT_OK_RESPONSE.result) {
|
|
|
|
logger.write(MODULE_NAME, "[ERROR] start -> writeRecordsFile -> onFileCreated -> err = " + err.stack);
|
|
logger.write(MODULE_NAME, "[ERROR] start -> writeRecordsFile -> onFileCreated -> filePath = " + filePath);
|
|
logger.write(MODULE_NAME, "[ERROR] start -> writeRecordsFile -> onFileCreated -> market = " + market.assetA + "_" + market.assetB);
|
|
|
|
callBackFunction(err);
|
|
return;
|
|
|
|
}
|
|
|
|
writeConditionsFile();
|
|
|
|
}
|
|
catch (err) {
|
|
logger.write(MODULE_NAME, "[ERROR] start -> writeRecordsFile -> onFileCreated -> err = " + err.stack);
|
|
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
|
|
}
|
|
}
|
|
}
|
|
catch (err) {
|
|
logger.write(MODULE_NAME, "[ERROR] start -> writeRecordsFile -> err = " + err.stack);
|
|
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
|
|
}
|
|
}
|
|
|
|
function writeConditionsFile() {
|
|
|
|
try {
|
|
|
|
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeConditionsFile -> Entering function."); }
|
|
|
|
let separator = "";
|
|
let fileRecordCounter = 0;
|
|
|
|
let fileContent = "";
|
|
|
|
for (let i = 0; i < conditionsArray.length; i++) {
|
|
|
|
let record = conditionsArray[i];
|
|
|
|
let conditions = "";
|
|
let conditionsSeparator = "";
|
|
|
|
for (let j = 0; j < record.length - 3; j++) {
|
|
conditions = conditions + conditionsSeparator + record[j];
|
|
if (conditionsSeparator === "") { conditionsSeparator = ","; }
|
|
}
|
|
|
|
conditions = conditions + conditionsSeparator + '[' + record[record.length - 3] + ']'; // The last item contains an Array of condition values.
|
|
conditions = conditions + conditionsSeparator + '[' + record[record.length - 2] + ']'; // The last item contains an Array of formulaErrors.
|
|
conditions = conditions + conditionsSeparator + '[' + record[record.length - 1] + ']'; // The last item contains an Array of formulaValues.
|
|
|
|
fileContent = fileContent + separator + '[' + conditions + ']';
|
|
|
|
if (separator === "") { separator = ","; }
|
|
|
|
fileRecordCounter++;
|
|
|
|
}
|
|
|
|
fileContent = "[" + fileContent + "]";
|
|
fileContent = "[" + JSON.stringify(tradingSystem) + "," + JSON.stringify(lastObjectsArray) + "," + fileContent + "]";
|
|
let fileName = '' + market.assetA + '_' + market.assetB + '.json';
|
|
|
|
let filePathRoot = bot.devTeam + "/" + bot.codeName + "." + bot.version.major + "." + bot.version.minor + "/" + global.CLONE_EXECUTOR.codeName + "." + global.CLONE_EXECUTOR.version + "/" + global.EXCHANGE_NAME + "/" + bot.dataSetVersion;
|
|
let filePath = filePathRoot + "/Output/" + CONDITIONS_FOLDER_NAME + "/" + "Multi-Period-Market" + "/" + outputPeriodLabel;
|
|
filePath += '/' + fileName
|
|
|
|
fileStorage.createTextFile(bot.devTeam, filePath, fileContent + '\n', onFileCreated);
|
|
|
|
function onFileCreated(err) {
|
|
|
|
try {
|
|
|
|
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeConditionsFile -> onFileCreated -> Entering function."); }
|
|
if (LOG_FILE_CONTENT === true) { logger.write(MODULE_NAME, "[INFO] start -> writeConditionsFile -> onFileCreated -> fileContent = " + fileContent); }
|
|
|
|
if (err.result !== global.DEFAULT_OK_RESPONSE.result) {
|
|
|
|
logger.write(MODULE_NAME, "[ERROR] start -> writeConditionsFile -> onFileCreated -> err = " + err.stack);
|
|
logger.write(MODULE_NAME, "[ERROR] start -> writeConditionsFile -> onFileCreated -> filePath = " + filePath);
|
|
logger.write(MODULE_NAME, "[ERROR] start -> writeConditionsFile -> onFileCreated -> market = " + market.assetA + "_" + market.assetB);
|
|
|
|
callBackFunction(err);
|
|
return;
|
|
|
|
}
|
|
|
|
writeStrategiesFile();
|
|
|
|
}
|
|
catch (err) {
|
|
logger.write(MODULE_NAME, "[ERROR] start -> writeConditionsFile -> onFileCreated -> err = " + err.stack);
|
|
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
|
|
}
|
|
}
|
|
}
|
|
catch (err) {
|
|
logger.write(MODULE_NAME, "[ERROR] start -> writeConditionsFile -> err = " + err.stack);
|
|
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
|
|
}
|
|
}
|
|
|
|
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.CLONE_EXECUTOR.codeName + "." + global.CLONE_EXECUTOR.version + "/" + global.EXCHANGE_NAME + "/" + bot.dataSetVersion;
|
|
let filePath = filePathRoot + "/Output/" + STRATEGIES_FOLDER_NAME + "/" + "Multi-Period-Market" + "/" + outputPeriodLabel;
|
|
filePath += '/' + fileName
|
|
|
|
fileStorage.createTextFile(bot.devTeam, filePath, 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.stack);
|
|
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.stack);
|
|
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
|
|
}
|
|
}
|
|
}
|
|
catch (err) {
|
|
logger.write(MODULE_NAME, "[ERROR] start -> writeStrategiesFile -> err = " + err.stack);
|
|
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];
|
|
if (record.stopRate === undefined) { record.stopRate = 0 }
|
|
|
|
fileContent = fileContent + separator + '[' +
|
|
record.begin + "," +
|
|
record.end + "," +
|
|
record.status + "," +
|
|
record.lastTradeROI + "," +
|
|
record.beginRate + "," +
|
|
record.endRate + "," +
|
|
record.exitType + "," +
|
|
record.stopRate + "]";
|
|
|
|
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.CLONE_EXECUTOR.codeName + "." + global.CLONE_EXECUTOR.version + "/" + global.EXCHANGE_NAME + "/" + bot.dataSetVersion;
|
|
let filePath = filePathRoot + "/Output/" + TRADES_FOLDER_NAME + "/" + "Multi-Period-Market" + "/" + outputPeriodLabel;
|
|
filePath += '/' + fileName
|
|
|
|
fileStorage.createTextFile(bot.devTeam, filePath, 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.stack);
|
|
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.stack);
|
|
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
|
|
}
|
|
}
|
|
}
|
|
catch (err) {
|
|
logger.write(MODULE_NAME, "[ERROR] start -> writeTradesFile -> err = " + err.stack);
|
|
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
|
|
}
|
|
}
|
|
|
|
}
|
|
catch (err) {
|
|
logger.write(MODULE_NAME, "[ERROR] start -> err = " + err.stack);
|
|
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
|
|
}
|
|
}
|
|
};
|