From 76eedc8baa7396724ecc6b09941e42361d20f90c Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Tue, 16 Jul 2019 01:25:11 +0200 Subject: [PATCH 01/19] INITIAL_BALANCE_ASSET_A mandatory or Error --- Context.js | 4 + package-lock.json | 230 +++++++++++++++++++++++----------------------- 2 files changed, 119 insertions(+), 115 deletions(-) diff --git a/Context.js b/Context.js index 5cbdf9fd9..3a2dae71a 100644 --- a/Context.js +++ b/Context.js @@ -302,9 +302,13 @@ runIndex = thisObject.statusReport.runs.length - 1; thisObject.executionHistory = []; + + if (process.env.INITIAL_BALANCE_ASSET_A === undefined || process.env.INITIAL_BALANCE_ASSET_B === undefined) { throw new Error("Environment Variables INITIAL_INVESTMENT_A and INITIAL_INVESTMENT_B cannot be undefined.") } + const INITIAL_INVESTMENT_A = Number(process.env.INITIAL_BALANCE_ASSET_A); const INITIAL_INVESTMENT_B = Number(process.env.INITIAL_BALANCE_ASSET_B); + thisObject.executionContext = { investment: { // This is used to calculate profits. assetA: INITIAL_INVESTMENT_A, diff --git a/package-lock.json b/package-lock.json index f4d72e256..8622aedc6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,13 +11,13 @@ "requires": { "axios": "0.19.0", "dotenv": "8.0.0", - "lodash": "^4.17.11", - "nonce": "^1.0.4", - "querystring": "^0.2.0", - "request": "^2.88.0", - "retry": "^0.12.0", - "superagent": "^5.0.2", - "underscore": "^1.9.1" + "lodash": "4.17.11", + "nonce": "1.0.4", + "querystring": "0.2.0", + "request": "2.88.0", + "retry": "0.12.0", + "superagent": "5.1.0", + "underscore": "1.9.1" } }, "@superalgos/mqservice": { @@ -30,10 +30,10 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.2.tgz", "integrity": "sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw==", "requires": { - "fast-deep-equal": "^2.0.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" + "fast-deep-equal": "2.0.1", + "fast-json-stable-stringify": "2.0.0", + "json-schema-traverse": "0.4.1", + "uri-js": "4.2.2" } }, "asn1": { @@ -41,7 +41,7 @@ "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", "requires": { - "safer-buffer": "~2.1.0" + "safer-buffer": "2.1.2" } }, "assert-plus": { @@ -54,7 +54,7 @@ "resolved": "https://registry.npmjs.org/async/-/async-2.6.2.tgz", "integrity": "sha512-H1qVYh1MYhEEFLsP97cVKqCGo7KfCyTt6uEWqsTBr9SO84oK9Uwbyd/yCW+6rKJLHksBNUVWZDAjfS+Ccx0Bbg==", "requires": { - "lodash": "^4.17.11" + "lodash": "4.17.11" } }, "asynckit": { @@ -78,7 +78,7 @@ "integrity": "sha512-1uvKqKQta3KBxIz14F2v06AEHZ/dIoeKfbTRkK1E5oqjDnuEerLmYTgJB5AiQZHJcljpg1TuRzdjDR06qNk0DQ==", "requires": { "follow-redirects": "1.5.10", - "is-buffer": "^2.0.2" + "is-buffer": "2.0.3" } }, "bcrypt-pbkdf": { @@ -86,7 +86,7 @@ "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", "requires": { - "tweetnacl": "^0.14.3" + "tweetnacl": "0.14.5" } }, "caseless": { @@ -99,8 +99,8 @@ "resolved": "https://registry.npmjs.org/color/-/color-3.0.0.tgz", "integrity": "sha512-jCpd5+s0s0t7p3pHQKpnJ0TpQKKdleP71LWcA0aqiljpiuAkOSUFN/dyH8ZwF0hRmFlrIuRhufds1QyEP9EB+w==", "requires": { - "color-convert": "^1.9.1", - "color-string": "^1.5.2" + "color-convert": "1.9.3", + "color-string": "1.5.3" } }, "color-convert": { @@ -121,8 +121,8 @@ "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.5.3.tgz", "integrity": "sha512-dC2C5qeWoYkxki5UAXapdjqO672AM4vZuPGRQfO8b5HKuKGBbKWpITyDYN7TOFKvRW7kOgAn3746clDBMDJyQw==", "requires": { - "color-name": "^1.0.0", - "simple-swizzle": "^0.2.2" + "color-name": "1.1.3", + "simple-swizzle": "0.2.2" } }, "colornames": { @@ -140,8 +140,8 @@ "resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.2.tgz", "integrity": "sha512-vt+OoIP2d76xLhjwbBaucYlNSpPsrJWPlBTtwCpQKIu6/CSMutyzX93O/Do0qzpH3YoHEes8YEFXyZ797rEhzQ==", "requires": { - "color": "3.0.x", - "text-hex": "1.0.x" + "color": "3.0.0", + "text-hex": "1.0.0" } }, "combined-stream": { @@ -149,7 +149,7 @@ "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", "requires": { - "delayed-stream": "~1.0.0" + "delayed-stream": "1.0.0" } }, "component-emitter": { @@ -172,7 +172,7 @@ "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", "requires": { - "assert-plus": "^1.0.0" + "assert-plus": "1.0.0" } }, "debug": { @@ -193,9 +193,9 @@ "resolved": "https://registry.npmjs.org/diagnostics/-/diagnostics-1.1.1.tgz", "integrity": "sha512-8wn1PmdunLJ9Tqbx+Fx/ZEuHfJf4NKSN2ZBj7SJC/OWRWha843+WsTjqMe1B5E3p28jqBlp+mJ2fPVxPyNgYKQ==", "requires": { - "colorspace": "1.1.x", - "enabled": "1.0.x", - "kuler": "1.0.x" + "colorspace": "1.1.2", + "enabled": "1.0.2", + "kuler": "1.0.1" } }, "dotenv": { @@ -208,8 +208,8 @@ "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", "requires": { - "jsbn": "~0.1.0", - "safer-buffer": "^2.1.0" + "jsbn": "0.1.1", + "safer-buffer": "2.1.2" } }, "enabled": { @@ -217,7 +217,7 @@ "resolved": "https://registry.npmjs.org/enabled/-/enabled-1.0.2.tgz", "integrity": "sha1-ll9lE9LC0cX0ZStkouM5ZGf8L5M=", "requires": { - "env-variable": "0.0.x" + "env-variable": "0.0.5" } }, "env-variable": { @@ -260,7 +260,7 @@ "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.10.tgz", "integrity": "sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==", "requires": { - "debug": "=3.1.0" + "debug": "3.1.0" } }, "forever-agent": { @@ -273,9 +273,9 @@ "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", - "mime-types": "^2.1.12" + "asynckit": "0.4.0", + "combined-stream": "1.0.8", + "mime-types": "2.1.24" } }, "formidable": { @@ -288,7 +288,7 @@ "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", "requires": { - "assert-plus": "^1.0.0" + "assert-plus": "1.0.0" } }, "har-schema": { @@ -301,8 +301,8 @@ "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz", "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==", "requires": { - "ajv": "^6.5.5", - "har-schema": "^2.0.0" + "ajv": "6.10.2", + "har-schema": "2.0.0" } }, "http-signature": { @@ -310,9 +310,9 @@ "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", "requires": { - "assert-plus": "^1.0.0", - "jsprim": "^1.2.2", - "sshpk": "^1.7.0" + "assert-plus": "1.0.0", + "jsprim": "1.4.1", + "sshpk": "1.16.1" } }, "is-arrayish": { @@ -381,7 +381,7 @@ "resolved": "https://registry.npmjs.org/kuler/-/kuler-1.0.1.tgz", "integrity": "sha512-J9nVUucG1p/skKul6DU3PUZrhs0LPulNaeUOox0IyXDi8S4CztTHs1gQphhuZmzXG7VOQSf6NJfKuzteQLv9gQ==", "requires": { - "colornames": "^1.1.1" + "colornames": "1.1.1" } }, "lodash": { @@ -394,11 +394,11 @@ "resolved": "https://registry.npmjs.org/logform/-/logform-2.1.2.tgz", "integrity": "sha512-+lZh4OpERDBLqjiwDLpAWNQu6KMjnlXH2ByZwCuSqVPJletw0kTWJf5CgSNAUKn1KUkv3m2cUz/LK8zyEy7wzQ==", "requires": { - "colors": "^1.2.1", - "fast-safe-stringify": "^2.0.4", - "fecha": "^2.3.3", - "ms": "^2.1.1", - "triple-beam": "^1.3.0" + "colors": "1.3.3", + "fast-safe-stringify": "2.0.6", + "fecha": "2.3.3", + "ms": "2.1.1", + "triple-beam": "1.3.0" }, "dependencies": { "ms": { @@ -486,9 +486,9 @@ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.4.0.tgz", "integrity": "sha512-jItXPLmrSR8jmTRmRWJXCnGJsfy85mB3Wd/uINMXA65yrnFo0cPClFIUWzo2najVNSl+mx7/4W8ttlLWJe99pQ==", "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" + "inherits": "2.0.3", + "string_decoder": "1.2.0", + "util-deprecate": "1.0.2" }, "dependencies": { "inherits": { @@ -503,26 +503,26 @@ "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz", "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==", "requires": { - "aws-sign2": "~0.7.0", - "aws4": "^1.8.0", - "caseless": "~0.12.0", - "combined-stream": "~1.0.6", - "extend": "~3.0.2", - "forever-agent": "~0.6.1", - "form-data": "~2.3.2", - "har-validator": "~5.1.0", - "http-signature": "~1.2.0", - "is-typedarray": "~1.0.0", - "isstream": "~0.1.2", - "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.19", - "oauth-sign": "~0.9.0", - "performance-now": "^2.1.0", - "qs": "~6.5.2", - "safe-buffer": "^5.1.2", - "tough-cookie": "~2.4.3", - "tunnel-agent": "^0.6.0", - "uuid": "^3.3.2" + "aws-sign2": "0.7.0", + "aws4": "1.8.0", + "caseless": "0.12.0", + "combined-stream": "1.0.8", + "extend": "3.0.2", + "forever-agent": "0.6.1", + "form-data": "2.3.3", + "har-validator": "5.1.3", + "http-signature": "1.2.0", + "is-typedarray": "1.0.0", + "isstream": "0.1.2", + "json-stringify-safe": "5.0.1", + "mime-types": "2.1.24", + "oauth-sign": "0.9.0", + "performance-now": "2.1.0", + "qs": "6.5.2", + "safe-buffer": "5.1.2", + "tough-cookie": "2.4.3", + "tunnel-agent": "0.6.0", + "uuid": "3.3.2" } }, "retry": { @@ -550,7 +550,7 @@ "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", "integrity": "sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo=", "requires": { - "is-arrayish": "^0.3.1" + "is-arrayish": "0.3.2" } }, "sshpk": { @@ -558,15 +558,15 @@ "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", "requires": { - "asn1": "~0.2.3", - "assert-plus": "^1.0.0", - "bcrypt-pbkdf": "^1.0.0", - "dashdash": "^1.12.0", - "ecc-jsbn": "~0.1.1", - "getpass": "^0.1.1", - "jsbn": "~0.1.0", - "safer-buffer": "^2.0.2", - "tweetnacl": "~0.14.0" + "asn1": "0.2.4", + "assert-plus": "1.0.0", + "bcrypt-pbkdf": "1.0.2", + "dashdash": "1.14.1", + "ecc-jsbn": "0.1.2", + "getpass": "0.1.7", + "jsbn": "0.1.1", + "safer-buffer": "2.1.2", + "tweetnacl": "0.14.5" } }, "stack-trace": { @@ -579,7 +579,7 @@ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.2.0.tgz", "integrity": "sha512-6YqyX6ZWEYguAxgZzHGL7SsCeGx3V2TtOTqZz1xSTSWnqsbWwbptafNyvf/ACquZUXV3DANr5BDIwNYe1mN42w==", "requires": { - "safe-buffer": "~5.1.0" + "safe-buffer": "5.1.2" } }, "superagent": { @@ -587,17 +587,17 @@ "resolved": "https://registry.npmjs.org/superagent/-/superagent-5.1.0.tgz", "integrity": "sha512-7V6JVx5N+eTL1MMqRBX0v0bG04UjrjAvvZJTF/VDH/SH2GjSLqlrcYepFlpTrXpm37aSY6h3GGVWGxXl/98TKA==", "requires": { - "component-emitter": "^1.3.0", - "cookiejar": "^2.1.2", - "debug": "^4.1.1", - "fast-safe-stringify": "^2.0.6", - "form-data": "^2.3.3", - "formidable": "^1.2.1", - "methods": "^1.1.2", - "mime": "^2.4.4", - "qs": "^6.7.0", - "readable-stream": "^3.4.0", - "semver": "^6.1.1" + "component-emitter": "1.3.0", + "cookiejar": "2.1.2", + "debug": "4.1.1", + "fast-safe-stringify": "2.0.6", + "form-data": "2.3.3", + "formidable": "1.2.1", + "methods": "1.1.2", + "mime": "2.4.4", + "qs": "6.7.0", + "readable-stream": "3.4.0", + "semver": "6.2.0" }, "dependencies": { "debug": { @@ -605,7 +605,7 @@ "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", "requires": { - "ms": "^2.1.1" + "ms": "2.1.2" } }, "ms": { @@ -630,8 +630,8 @@ "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz", "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==", "requires": { - "psl": "^1.1.24", - "punycode": "^1.4.1" + "psl": "1.2.0", + "punycode": "1.4.1" }, "dependencies": { "punycode": { @@ -651,7 +651,7 @@ "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", "requires": { - "safe-buffer": "^5.0.1" + "safe-buffer": "5.1.2" } }, "tweetnacl": { @@ -669,7 +669,7 @@ "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", "requires": { - "punycode": "^2.1.0" + "punycode": "2.1.1" } }, "util-deprecate": { @@ -687,9 +687,9 @@ "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", "requires": { - "assert-plus": "^1.0.0", + "assert-plus": "1.0.0", "core-util-is": "1.0.2", - "extsprintf": "^1.2.0" + "extsprintf": "1.3.0" } }, "winston": { @@ -697,15 +697,15 @@ "resolved": "https://registry.npmjs.org/winston/-/winston-3.2.1.tgz", "integrity": "sha512-zU6vgnS9dAWCEKg/QYigd6cgMVVNwyTzKs81XZtTFuRwJOcDdBg7AU0mXVyNbs7O5RH2zdv+BdNZUlx7mXPuOw==", "requires": { - "async": "^2.6.1", - "diagnostics": "^1.1.1", - "is-stream": "^1.1.0", - "logform": "^2.1.1", + "async": "2.6.2", + "diagnostics": "1.1.1", + "is-stream": "1.1.0", + "logform": "2.1.2", "one-time": "0.0.4", - "readable-stream": "^3.1.1", - "stack-trace": "0.0.x", - "triple-beam": "^1.3.0", - "winston-transport": "^4.3.0" + "readable-stream": "3.4.0", + "stack-trace": "0.0.10", + "triple-beam": "1.3.0", + "winston-transport": "4.3.0" } }, "winston-transport": { @@ -713,8 +713,8 @@ "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.3.0.tgz", "integrity": "sha512-B2wPuwUi3vhzn/51Uukcao4dIduEiPOcOt9HJ3QeaXgkJ5Z7UwpBzxS4ZGNHtrxrUvTwemsQiSys0ihOf8Mp1A==", "requires": { - "readable-stream": "^2.3.6", - "triple-beam": "^1.2.0" + "readable-stream": "2.3.6", + "triple-beam": "1.3.0" }, "dependencies": { "inherits": { @@ -727,13 +727,13 @@ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "2.0.0", + "safe-buffer": "5.1.2", + "string_decoder": "1.1.1", + "util-deprecate": "1.0.2" } }, "string_decoder": { @@ -741,7 +741,7 @@ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "requires": { - "safe-buffer": "~5.1.0" + "safe-buffer": "5.1.2" } } } From fc48cfd5e6957c404acd4a9b9fea027708f6b3ff Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Tue, 16 Jul 2019 11:50:37 +0200 Subject: [PATCH 02/19] Trading Engine Loop Implemented --- Project.njsproj | 3 + Root.js | 204 ++++--- TradingEngineProcessMainLoop.js | 928 ++++++++++++++++++++++++++++++++ 3 files changed, 1067 insertions(+), 68 deletions(-) create mode 100644 TradingEngineProcessMainLoop.js diff --git a/Project.njsproj b/Project.njsproj index 2c9ca85ab..b286cdfa9 100644 --- a/Project.njsproj +++ b/Project.njsproj @@ -47,6 +47,9 @@ Code + + Code + Code diff --git a/Root.js b/Root.js index ef5e0b714..3f85db528 100644 --- a/Root.js +++ b/Root.js @@ -149,6 +149,7 @@ const TRADING_BOT_MAIN_LOOP_MODULE = require('./TradingBotProcessMainLoop'); const INDICATOR_BOT_MAIN_LOOP_MODULE = require('./IndicatorBotProcessMainLoop'); const EXTRACTION_BOT_MAIN_LOOP_MODULE = require('./SensorBotProcessMainLoop'); + const TRADING_ENGINE_MAIN_LOOP_MODULE = require('./TradingEngineProcessMainLoop'); /* Loop through all the processes configured to be run by this Node.js Instance. */ @@ -353,6 +354,10 @@ runIndicatorBot(botConfig, processConfig, month, year); break; } + case 'Trading-Engine': { + runTradingEngine(botConfig, processConfig); + break; + } default: { console.log(logDisplace + "Root : [ERROR] start -> findProcess -> Unexpected bot type. -> botConfig.type = " + botConfig.type); } @@ -546,74 +551,6 @@ } } - function runIndicatorBot(pBotConfig, pProcessConfig, pMonth, pYear) { - - try { - const DEBUG_MODULE = require(ROOT_DIR + 'DebugLog'); - let logger; - - logger = DEBUG_MODULE.newDebugLog(); - logger.bot = pBotConfig; - - if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> findProcess -> runIndicatorBot -> Entering function."); } - if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> findProcess -> runIndicatorBot -> pMonth = " + pMonth); } - if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> findProcess -> runIndicatorBot -> pYear = " + pYear); } - - let indicatorBotMainLoop = INDICATOR_BOT_MAIN_LOOP_MODULE.newIndicatorBotProcessMainLoop(pBotConfig, logger); - indicatorBotMainLoop.initialize(UI_COMMANDS, pProcessConfig, onInitializeReady); - - function onInitializeReady(err) { - - if (err.result === global.DEFAULT_OK_RESPONSE.result) { - - indicatorBotMainLoop.run(pMonth, pYear, whenRunFinishes); - - function whenRunFinishes(err) { - - pBotConfig.loopCounter = 0; - - let botId; - if (pYear !== undefined) { - botId = pBotConfig.devTeam + "." + pBotConfig.codeName + "." + pBotConfig.process + "." + pYear + "." + pMonth; - } else { - botId = pBotConfig.devTeam + "." + pBotConfig.codeName + "." + pBotConfig.process; - } - - if (err.result === global.DEFAULT_OK_RESPONSE.result) { - - logger.write(MODULE_NAME, "[INFO] start -> findProcess -> runIndicatorBot -> onInitializeReady -> whenStartFinishes -> Bot execution finished sucessfully."); - logger.write(MODULE_NAME, "[INFO] start -> findProcess -> runIndicatorBot -> onInitializeReady -> whenStartFinishes -> Bot Id = " + botId); - - console.log(logDisplace + "Root : [INFO] start -> findProcess -> runIndicatorBot -> onInitializeReady -> whenStartFinishes -> botId = " + botId); - console.log(logDisplace + "Root : [INFO] start -> findProcess -> runIndicatorBot -> onInitializeReady -> whenStartFinishes -> Bot execution finished sucessfully."); - - logger.persist(); - - } else { - - logger.write(MODULE_NAME, "[ERROR] start -> findProcess -> runIndicatorBot -> onInitializeReady -> whenStartFinishes -> err = ", err); - logger.write(MODULE_NAME, "[ERROR] start -> findProcess -> runIndicatorBot -> onInitializeReady -> whenStartFinishes -> Execution will be stopped. "); - logger.write(MODULE_NAME, "[ERROR] start -> findProcess -> runIndicatorBot -> onInitializeReady -> whenStartFinishes -> Bye."); - logger.write(MODULE_NAME, "[ERROR] start -> findProcess -> runIndicatorBot -> onInitializeReady -> whenStartFinishes -> Bot Id = " + botId); - console.log(logDisplace + "Root : [ERROR] start -> findProcess -> runIndicatorBot -> onInitializeReady -> whenStartFinishes -> Bot execution finished with errors. Please check the logs."); - logger.persist(); - } - } - - } else { - logger.write(MODULE_NAME, "[ERROR] start -> findProcess -> runIndicatorBot -> onInitializeReady -> err = ", err); - logger.write(MODULE_NAME, "[ERROR] start -> findProcess -> runIndicatorBot -> onInitializeReady -> Failed to initialize the bot. "); - console.log(logDisplace + "Root : [ERROR] start -> findProcess -> runIndicatorBot -> onInitializeReady -> err = ", err); - logger.persist(); - } - } - } - catch (err) { - console.log(logDisplace + "Root : [ERROR] start -> findProcess -> runIndicatorBot -> err = ", err); - - } - } - function runTradingBot(pBotConfig, pProcessConfig) { try { @@ -792,6 +729,137 @@ } } + function runIndicatorBot(pBotConfig, pProcessConfig, pMonth, pYear) { + + try { + const DEBUG_MODULE = require(ROOT_DIR + 'DebugLog'); + let logger; + + logger = DEBUG_MODULE.newDebugLog(); + logger.bot = pBotConfig; + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> findProcess -> runIndicatorBot -> Entering function."); } + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> findProcess -> runIndicatorBot -> pMonth = " + pMonth); } + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> findProcess -> runIndicatorBot -> pYear = " + pYear); } + + let indicatorBotMainLoop = INDICATOR_BOT_MAIN_LOOP_MODULE.newIndicatorBotProcessMainLoop(pBotConfig, logger); + indicatorBotMainLoop.initialize(UI_COMMANDS, pProcessConfig, onInitializeReady); + + function onInitializeReady(err) { + + if (err.result === global.DEFAULT_OK_RESPONSE.result) { + + indicatorBotMainLoop.run(pMonth, pYear, whenRunFinishes); + + function whenRunFinishes(err) { + + pBotConfig.loopCounter = 0; + + let botId; + if (pYear !== undefined) { + botId = pBotConfig.devTeam + "." + pBotConfig.codeName + "." + pBotConfig.process + "." + pYear + "." + pMonth; + } else { + botId = pBotConfig.devTeam + "." + pBotConfig.codeName + "." + pBotConfig.process; + } + + if (err.result === global.DEFAULT_OK_RESPONSE.result) { + + logger.write(MODULE_NAME, "[INFO] start -> findProcess -> runIndicatorBot -> onInitializeReady -> whenStartFinishes -> Bot execution finished sucessfully."); + logger.write(MODULE_NAME, "[INFO] start -> findProcess -> runIndicatorBot -> onInitializeReady -> whenStartFinishes -> Bot Id = " + botId); + + console.log(logDisplace + "Root : [INFO] start -> findProcess -> runIndicatorBot -> onInitializeReady -> whenStartFinishes -> botId = " + botId); + console.log(logDisplace + "Root : [INFO] start -> findProcess -> runIndicatorBot -> onInitializeReady -> whenStartFinishes -> Bot execution finished sucessfully."); + + logger.persist(); + + } else { + + logger.write(MODULE_NAME, "[ERROR] start -> findProcess -> runIndicatorBot -> onInitializeReady -> whenStartFinishes -> err = ", err); + logger.write(MODULE_NAME, "[ERROR] start -> findProcess -> runIndicatorBot -> onInitializeReady -> whenStartFinishes -> Execution will be stopped. "); + logger.write(MODULE_NAME, "[ERROR] start -> findProcess -> runIndicatorBot -> onInitializeReady -> whenStartFinishes -> Bye."); + logger.write(MODULE_NAME, "[ERROR] start -> findProcess -> runIndicatorBot -> onInitializeReady -> whenStartFinishes -> Bot Id = " + botId); + console.log(logDisplace + "Root : [ERROR] start -> findProcess -> runIndicatorBot -> onInitializeReady -> whenStartFinishes -> Bot execution finished with errors. Please check the logs."); + logger.persist(); + } + } + + } else { + logger.write(MODULE_NAME, "[ERROR] start -> findProcess -> runIndicatorBot -> onInitializeReady -> err = ", err); + logger.write(MODULE_NAME, "[ERROR] start -> findProcess -> runIndicatorBot -> onInitializeReady -> Failed to initialize the bot. "); + console.log(logDisplace + "Root : [ERROR] start -> findProcess -> runIndicatorBot -> onInitializeReady -> err = ", err); + logger.persist(); + } + } + } + catch (err) { + console.log(logDisplace + "Root : [ERROR] start -> findProcess -> runIndicatorBot -> err = ", err); + + } + } + + function runTradingEngine(pBotConfig, pProcessConfig) { + + try { + const DEBUG_MODULE = require(ROOT_DIR + 'DebugLog'); + let logger; + + logger = DEBUG_MODULE.newDebugLog(); + logger.bot = pBotConfig; + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> findProcess -> runTradingEngine -> Entering function."); } + + let tradingEngineMainLoop = TRADING_ENGINE_MAIN_LOOP_MODULE.newTradingEngineProcessMainLoop(pBotConfig, logger); + tradingEngineMainLoop.initialize(UI_COMMANDS, pProcessConfig, onInitializeReady); + + function onInitializeReady(err) { + + if (err.result === global.DEFAULT_OK_RESPONSE.result) { + + tradingEngineMainLoop.run(whenRunFinishes); + + function whenRunFinishes(err) { + + pBotConfig.loopCounter = 0; + + let botId; + + botId = pBotConfig.devTeam + "." + pBotConfig.codeName + "." + pBotConfig.process; + + if (err.result === global.DEFAULT_OK_RESPONSE.result) { + + logger.write(MODULE_NAME, "[INFO] start -> findProcess -> runTradingEngine -> onInitializeReady -> whenStartFinishes -> Bot execution finished sucessfully."); + logger.write(MODULE_NAME, "[INFO] start -> findProcess -> runTradingEngine -> onInitializeReady -> whenStartFinishes -> Bot Id = " + botId); + + console.log(logDisplace + "Root : [INFO] start -> findProcess -> runTradingEngine -> onInitializeReady -> whenStartFinishes -> botId = " + botId); + console.log(logDisplace + "Root : [INFO] start -> findProcess -> runTradingEngine -> onInitializeReady -> whenStartFinishes -> Bot execution finished sucessfully."); + + logger.persist(); + + } else { + + logger.write(MODULE_NAME, "[ERROR] start -> findProcess -> runTradingEngine -> onInitializeReady -> whenStartFinishes -> err = ", err); + logger.write(MODULE_NAME, "[ERROR] start -> findProcess -> runTradingEngine -> onInitializeReady -> whenStartFinishes -> Execution will be stopped. "); + logger.write(MODULE_NAME, "[ERROR] start -> findProcess -> runTradingEngine -> onInitializeReady -> whenStartFinishes -> Bye."); + logger.write(MODULE_NAME, "[ERROR] start -> findProcess -> runTradingEngine -> onInitializeReady -> whenStartFinishes -> Bot Id = " + botId); + console.log(logDisplace + "Root : [ERROR] start -> findProcess -> runTradingEngine -> onInitializeReady -> whenStartFinishes -> Bot execution finished with errors. Please check the logs."); + logger.persist(); + } + } + + } else { + logger.write(MODULE_NAME, "[ERROR] start -> findProcess -> runTradingEngine -> onInitializeReady -> err = ", err); + logger.write(MODULE_NAME, "[ERROR] start -> findProcess -> runTradingEngine -> onInitializeReady -> Failed to initialize the bot. "); + console.log(logDisplace + "Root : [ERROR] start -> findProcess -> runTradingEngine -> onInitializeReady -> err = ", err); + logger.persist(); + } + } + } + catch (err) { + console.log(logDisplace + "Root : [ERROR] start -> findProcess -> runTradingEngine -> err = ", err); + + } + } + } catch (err) { console.log(logDisplace + "Root : [ERROR] start -> findProcess -> err.message = " + err.message); diff --git a/TradingEngineProcessMainLoop.js b/TradingEngineProcessMainLoop.js new file mode 100644 index 000000000..d20e893b9 --- /dev/null +++ b/TradingEngineProcessMainLoop.js @@ -0,0 +1,928 @@ +exports.newTradingEngineProcessMainLoop = function newTradingEngineProcessMainLoop(bot, parentLogger) { + + const ROOT_DIR = './'; + + const MODULE_NAME = "Trading Engine Process Main Loop"; + const FULL_LOG = true; + + let USER_BOT_MODULE; + let COMMONS_MODULE; + + const MULTI_PERIOD_MARKET = require(ROOT_DIR + 'MultiPeriodMarket'); + const MULTI_PERIOD_DAILY = require(ROOT_DIR + 'MultiPeriodDaily'); + const FILE_STORAGE = require('./Integrations/FileStorage.js'); + let fileStorage = FILE_STORAGE.newFileStorage(); + + const DEBUG_MODULE = require(ROOT_DIR + 'DebugLog'); + let logger; // We need this here in order for the loopHealth function to work and be able to rescue the loop when it gets in trouble. + + let nextLoopTimeoutHandle; + let checkLoopHealthHandle; + + let thisObject = { + initialize: initialize, + run: run + }; + + let processConfig; + let UI_COMMANDS; + + return thisObject; + + function initialize(pUI_COMMANDS, pProcessConfig, callBackFunction) { + + /* This function is exactly the same in the 3 modules representing the 2 different bot types loops. */ + + try { + if (FULL_LOG === true) { parentLogger.write(MODULE_NAME, "[INFO] initialize -> Entering function."); } + + UI_COMMANDS = pUI_COMMANDS; + processConfig = pProcessConfig; + + let filePath = global.DEV_TEAM + "/" + "bots" + "/" + bot.repo + "/" + pProcessConfig.name + filePath += "/User.Bot.js" + + fileStorage.getTextFile(global.DEV_TEAM, filePath, onBotDownloaded); + + function onBotDownloaded(err, text) { + + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + + parentLogger.write(MODULE_NAME, "[ERROR] initialize -> onInizialized -> onBotDownloaded -> err.message = " + err.message); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return; + } + + USER_BOT_MODULE = {} + USER_BOT_MODULE.newUserBot = eval(text); // TODO This needs to be changed function + + filePath = global.DEV_TEAM + "/" + "bots" + "/" + bot.repo; + filePath += "/Commons.js" + + fileStorage.getTextFile(global.DEV_TEAM, filePath, onCommonsDownloaded); + + function onCommonsDownloaded(err, text) { + + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + parentLogger.write(MODULE_NAME, "[WARN] initialize -> onBotDownloaded -> onCommonsDownloaded -> Commons not found: " + err.code || err.message); + callBackFunction(global.DEFAULT_OK_RESPONSE); + return; + } + COMMONS_MODULE = {} + COMMONS_MODULE.newCommons = eval(text); // TODO This needs to be changed function + + callBackFunction(global.DEFAULT_OK_RESPONSE); + } + } + } catch (err) { + parentLogger.write(MODULE_NAME, "[ERROR] initialize -> err = ", err); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function run(callBackFunction) { + + try { + + if (FULL_LOG === true) { parentLogger.write(MODULE_NAME, "[INFO] run -> Entering function."); } + + bot.enableCheckLoopHealth = true; + + let fixedTimeLoopIntervalHandle; + + if (bot.runAtFixedInterval === true) { + + fixedTimeLoopIntervalHandle = setInterval(loop, bot.fixedInterval); + + } else { + + loop(); + + } + + function loop() { + + try { + + function pad(str, max) { + str = str.toString(); + return str.length < max ? pad(" " + str, max) : str; + } + + /* For each loop we want to create a new log file. */ + + logger = DEBUG_MODULE.newDebugLog(); + logger.bot = bot; + logger.initialize(); + + bot.loopCounter++; + bot.loopStartTime = new Date().valueOf(); + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> Entering function."); } + + /* We define here all the modules that the rest of the infraestructure, including the bots themselves can consume. */ + + const UTILITIES = require(ROOT_DIR + 'CloudUtilities'); + const STATUS_REPORT = require(ROOT_DIR + 'StatusReport'); + const STATUS_DEPENDENCIES = require(ROOT_DIR + 'StatusDependencies'); + const DATA_DEPENDENCIES = require(ROOT_DIR + 'DataDependencies'); + const DATA_SET = require(ROOT_DIR + 'DataSet'); + + /* We define the datetime for the process that we are running now. This will be the official processing time for both the infraestructure and the bot. */ + + bot.processDatetime = new Date(); // This will be considered the process date and time, so as to have it consistenly all over the execution. + + /* High level log entry */ + + console.log(new Date().toISOString() + " " + pad(bot.codeName, 20) + " " + pad(bot.process, 30) + + " Entered into Main Loop # " + pad(Number(bot.loopCounter), 8)); + + /* We will prepare first the infraestructure needed for the bot to run. There are 3 modules we need to sucessfullly initialize first. */ + + let userBot; + let processFramework; + let statusDependencies; + let dataDependencies; + + let nextWaitTime; + + initializeStatusDependencies(); + + function initializeStatusDependencies() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> initializeStatusDependencies -> Entering function."); } + + statusDependencies = STATUS_DEPENDENCIES.newStatusDependencies(bot, logger, STATUS_REPORT, UTILITIES); + + statusDependencies.initialize(processConfig.statusDependencies, undefined, undefined, onInizialized); + + function onInizialized(err) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> initializeStatusDependencies -> onInizialized -> Entering function."); } + + switch (err.result) { + case global.DEFAULT_OK_RESPONSE.result: { + logger.write(MODULE_NAME, "[INFO] run -> loop -> initializeStatusDependencies -> onInizialized -> Execution finished well."); + initializeDataDependencies(); + return; + } + case global.DEFAULT_RETRY_RESPONSE.result: { // Something bad happened, but if we retry in a while it might go through the next time. + logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeStatusDependencies -> onInizialized -> Retry Later. Requesting Execution Retry."); + nextWaitTime = 'Retry'; + loopControl(nextWaitTime); + return; + } + case global.DEFAULT_FAIL_RESPONSE.result: { // This is an unexpected exception that we do not know how to handle. + logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeStatusDependencies -> onInizialized -> Operation Failed. Aborting the process."); + + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + return; + } + default: { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeStatusDependencies -> onInizialized -> Unhandled err.result received. -> err.result = " + err.result); + logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeStatusDependencies -> onInizialized -> Unhandled err.result received. -> err.message = " + err.message); + + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return; + } + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeStatusDependencies -> onInizialized -> err = ", err); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + } + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeStatusDependencies -> err = ", err); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + } + } + + function initializeDataDependencies() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> initializeDataDependencies -> Entering function."); } + + dataDependencies = DATA_DEPENDENCIES.newDataDependencies(bot, logger, DATA_SET); + + dataDependencies.initialize(processConfig.dataDependencies, onInizialized); + + function onInizialized(err) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> initializeDataDependencies -> onInizialized -> Entering function."); } + + switch (err.result) { + case global.DEFAULT_OK_RESPONSE.result: { + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> initializeDataDependencies -> onInizialized -> Execution finished well."); } + + /* If the process is configured to run inside a framework, we continue there, otherwise we run the bot directly. */ + if (processConfig.framework === undefined) { + initializeUserBot(); + return; + } + + switch (processConfig.framework.name) { + case 'Multi-Period-Market': { + processFramework = MULTI_PERIOD_MARKET.newMultiPeriodMarket(bot, logger, COMMONS_MODULE, UTILITIES, USER_BOT_MODULE, COMMONS_MODULE); + intitializeProcessFramework(); + break; + } + case 'Multi-Period-Daily': { + processFramework = MULTI_PERIOD_DAILY.newMultiPeriodDaily(bot, logger, COMMONS_MODULE, UTILITIES, USER_BOT_MODULE, COMMONS_MODULE); + intitializeProcessFramework(); + break; + } + default: { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeDataDependencies -> onInizialized -> Process Framework not Supported."); + logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeDataDependencies -> onInizialized -> Process Framework Name = " + processConfig.framework.name); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + return; + } + } + return; + } + case global.DEFAULT_RETRY_RESPONSE.result: { // Something bad happened, but if we retry in a while it might go through the next time. + logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeDataDependencies -> onInizialized -> Retry Later. Requesting Execution Retry."); + nextWaitTime = 'Retry'; + loopControl(nextWaitTime); + return; + } + case global.DEFAULT_FAIL_RESPONSE.result: { // This is an unexpected exception that we do not know how to handle. + logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeDataDependencies -> onInizialized -> Operation Failed. Aborting the process."); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + return; + } + default: { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeDataDependencies -> onInizialized -> Unhandled err.result received. -> err.result = " + err.result); + logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeDataDependencies -> onInizialized -> Unhandled err.result received. -> err.message = " + err.message); + + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return; + } + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeDataDependencies -> onInizialized -> err = ", err); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + } + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeDataDependencies -> err = ", err); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + } + } + + function initializeUserBot() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> initializeUserBot -> Entering function."); } + + usertBot = USER_BOT_MODULE.newUserBot(bot, logger, COMMONS_MODULE, UTILITIES, fileStorage); + + usertBot.initialize(statusDependencies, undefined, undefined, onInizialized); + + function onInizialized(err) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> initializeUserBot -> onInizialized -> Entering function."); } + + switch (err.result) { + case global.DEFAULT_OK_RESPONSE.result: { + logger.write(MODULE_NAME, "[INFO] run -> loop -> initializeUserBot -> onInizialized -> Execution finished well."); + startUserBot(); + return; + } + case global.DEFAULT_RETRY_RESPONSE.result: { // Something bad happened, but if we retry in a while it might go through the next time. + logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeUserBot -> onInizialized -> Retry Later. Requesting Execution Retry."); + nextWaitTime = 'Retry'; + loopControl(nextWaitTime); + return; + } + case global.DEFAULT_FAIL_RESPONSE.result: { // This is an unexpected exception that we do not know how to handle. + logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeUserBot -> onInizialized -> Operation Failed. Aborting the process."); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + return; + } + case global.CUSTOM_OK_RESPONSE.result: { + + switch (err.message) { + case "Too far in the future.": { + logger.write(MODULE_NAME, "[WARN] run -> loop -> initializeUserBot -> onInizialized > Too far in the future. This Loop will enter in coma."); + nextWaitTime = 'Coma'; + loopControl(nextWaitTime); + return; + } + case "Not needed now, but soon.": { + logger.write(MODULE_NAME, "[WARN] run -> loop -> initializeUserBot -> onInizialized > Not needed now, but soon. This Loop will continue with Normal wait time."); + nextWaitTime = 'Normal'; + loopControl(nextWaitTime); + return; + } + default: { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeUserBot -> onInizialized > Unhandled custom response received. -> err.message = " + err.message); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + return; + } + } + } + default: { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeUserBot -> onInizialized -> Unhandled err.result received. -> err.result = " + err.result); + logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeUserBot -> onInizialized -> Unhandled err.result received. -> err.message = " + err.message); + + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return; + } + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeUserBot -> onInizialized -> err = ", err); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + } + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeUserBot -> err = ", err); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + } + } + + function startUserBot() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> startUserBot -> Entering function."); } + + usertBot.start(onFinished); + + function onFinished(err) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> startUserBot -> onFinished -> Entering function."); } + + switch (err.result) { + case global.DEFAULT_OK_RESPONSE.result: { + logger.write(MODULE_NAME, "[INFO] run -> loop -> startUserBot -> onFinished -> Execution finished well."); + nextWaitTime = 'Normal'; + loopControl(nextWaitTime); + return; + } + case global.DEFAULT_RETRY_RESPONSE.result: { // Something bad happened, but if we retry in a while it might go through the next time. + logger.write(MODULE_NAME, "[ERROR] run -> loop -> startUserBot -> onFinished -> Retry Later. Requesting Execution Retry."); + nextWaitTime = 'Retry'; + loopControl(nextWaitTime); + return; + } + case global.DEFAULT_FAIL_RESPONSE.result: { // This is an unexpected exception that we do not know how to handle. + logger.write(MODULE_NAME, "[ERROR] run -> loop -> startUserBot -> onFinished -> Operation Failed. Aborting the process."); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + return; + } + case global.CUSTOM_OK_RESPONSE.result: { + + switch (err.message) { + case "Dependency does not exist.": { + logger.write(MODULE_NAME, "[WARN] run -> loop -> startUserBot -> onFinished -> Dependency does not exist. This Loop will go to sleep."); + nextWaitTime = 'Sleep'; + loopControl(nextWaitTime); + return; + } + case "Dependency not ready.": { + logger.write(MODULE_NAME, "[WARN] run -> loop -> startUserBot -> onFinished -> Dependency not ready. This Loop will go to sleep."); + nextWaitTime = 'Sleep'; + loopControl(nextWaitTime); + return; + } + case "Month before it is needed.": { + logger.write(MODULE_NAME, "[WARN] run -> loop -> startUserBot -> onFinished -> Month before it is needed. This Loop will be terminated."); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(global.DEFAULT_OK_RESPONSE); + return; + } + case "Month fully processed.": { + logger.write(MODULE_NAME, "[WARN] run -> loop -> startUserBot -> onFinished -> Month fully processed. This Loop will be terminated."); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(global.DEFAULT_OK_RESPONSE); + return; + } + case "End of the month reached.": { + logger.write(MODULE_NAME, "[WARN] run -> loop -> startUserBot -> onFinished -> End of the month reached. This Loop will be terminated."); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(global.DEFAULT_OK_RESPONSE); + return; + } + default: { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> startUserBot -> onFinished -> Unhandled custom response received. -> err.message = " + err.message); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + return; + } + } + } + default: { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> startUserBot -> onFinished -> Unhandled err.result received. -> err.result = " + err.result); + logger.write(MODULE_NAME, "[ERROR] run -> loop -> startUserBot -> onFinished -> Unhandled err.result received. -> err.message = " + err.message); + + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return; + } + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> startUserBot -> onFinished -> err = ", err); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + } + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> startUserBot -> err = ", err); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + } + } + + function intitializeProcessFramework() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> intitializeProcessFramework -> Entering function."); } + + processFramework.initialize(processConfig, statusDependencies, dataDependencies, onInizialized, dataDependencies); + + function onInizialized(err) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> intitializeProcessFramework -> onInizialized -> Entering function."); } + + switch (err.result) { + case global.DEFAULT_OK_RESPONSE.result: { + logger.write(MODULE_NAME, "[INFO] run -> loop -> intitializeProcessFramework -> onInizialized -> Execution finished well."); + startProcessFramework(); + return; + } + case global.DEFAULT_RETRY_RESPONSE.result: { // Something bad happened, but if we retry in a while it might go through the next time. + logger.write(MODULE_NAME, "[ERROR] run -> loop -> intitializeProcessFramework -> onInizialized -> Retry Later. Requesting Execution Retry."); + nextWaitTime = 'Retry'; + loopControl(nextWaitTime); + return; + } + case global.DEFAULT_FAIL_RESPONSE.result: { // This is an unexpected exception that we do not know how to handle. + logger.write(MODULE_NAME, "[ERROR] run -> loop -> intitializeProcessFramework -> onInizialized -> Operation Failed. Aborting the process."); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + return; + } + case global.CUSTOM_OK_RESPONSE.result: { + + switch (err.message) { + case "Too far in the future.": { + logger.write(MODULE_NAME, "[WARN] run -> loop -> intitializeProcessFramework -> onInizialized > Too far in the future. This Loop will enter in coma."); + nextWaitTime = 'Coma'; + loopControl(nextWaitTime); + return; + } + case "Not needed now, but soon.": { + logger.write(MODULE_NAME, "[WARN] run -> loop -> intitializeProcessFramework -> onInizialized > Not needed now, but soon. This Loop will continue with Normal wait time."); + nextWaitTime = 'Normal'; + loopControl(nextWaitTime); + return; + } + default: { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> intitializeProcessFramework -> onInizialized > Unhandled custom response received. -> err.message = " + err.message); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + return; + } + } + } + default: { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> intitializeProcessFramework -> onInizialized -> Unhandled err.result received. -> err.result = " + err.result); + logger.write(MODULE_NAME, "[ERROR] run -> loop -> intitializeProcessFramework -> onInizialized -> Unhandled err.result received. -> err.message = " + err.message); + + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return; + } + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> intitializeProcessFramework -> onInizialized -> err = ", err); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + } + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> intitializeProcessFramework -> err = ", err); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + } + } + + function startProcessFramework() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> startProcessFramework -> Entering function."); } + + processFramework.start(onFinished); + + function onFinished(err) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> startProcessFramework -> onFinished -> Entering function."); } + + switch (err.result) { + case global.DEFAULT_OK_RESPONSE.result: { + logger.write(MODULE_NAME, "[INFO] run -> loop -> startProcessFramework -> onFinished -> Execution finished well."); + nextWaitTime = 'Normal'; + loopControl(nextWaitTime); + return; + } + case global.DEFAULT_RETRY_RESPONSE.result: { // Something bad happened, but if we retry in a while it might go through the next time. + logger.write(MODULE_NAME, "[ERROR] run -> loop -> startProcessFramework -> onFinished -> Retry Later. Requesting Execution Retry."); + nextWaitTime = 'Retry'; + loopControl(nextWaitTime); + return; + } + case global.DEFAULT_FAIL_RESPONSE.result: { // This is an unexpected exception that we do not know how to handle. + logger.write(MODULE_NAME, "[ERROR] run -> loop -> startProcessFramework -> onFinished -> Operation Failed. Aborting the process."); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + return; + } + case global.CUSTOM_OK_RESPONSE.result: { + + switch (err.message) { + case "Dependency does not exist.": { + logger.write(MODULE_NAME, "[WARN] run -> loop -> startProcessFramework -> onFinished -> Dependency does not exist. This Loop will go to sleep."); + nextWaitTime = 'Sleep'; + loopControl(nextWaitTime); + return; + } + case "Dependency not ready.": { + logger.write(MODULE_NAME, "[WARN] run -> loop -> startProcessFramework -> onFinished -> Dependency not ready. This Loop will go to sleep."); + nextWaitTime = 'Sleep'; + loopControl(nextWaitTime); + return; + } + case "Month before it is needed.": { + logger.write(MODULE_NAME, "[WARN] run -> loop -> startProcessFramework -> onFinished -> Month before it is needed. This Loop will be terminated."); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(global.DEFAULT_OK_RESPONSE); + return; + } + case "Month fully processed.": { + logger.write(MODULE_NAME, "[WARN] run -> loop -> startProcessFramework -> onFinished -> Month fully processed. This Loop will be terminated."); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(global.DEFAULT_OK_RESPONSE); + return; + } + case "End of the month reached.": { + logger.write(MODULE_NAME, "[WARN] run -> loop -> startProcessFramework -> onFinished -> End of the month reached. This Loop will be terminated."); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(global.DEFAULT_OK_RESPONSE); + return; + } + default: { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> startProcessFramework -> onFinished -> Unhandled custom response received. -> err.message = " + err.message); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + return; + } + } + } + default: { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> startProcessFramework -> onFinished -> Unhandled err.result received. -> err.result = " + err.result); + logger.write(MODULE_NAME, "[ERROR] run -> loop -> startProcessFramework -> onFinished -> Unhandled err.result received. -> err.message = " + err.message); + + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return; + } + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> startProcessFramework -> onFinished -> err = ", err); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + } + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> startProcessFramework -> err = ", err); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + } + } + + function loopControl(nextWaitTime) { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> loopControl -> Entering function."); } + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> loopControl -> nextWaitTime = " + nextWaitTime); } + + /* Here we check if we must stop the loop gracefully. */ + + shallWeStop(onStop, onContinue); + + function onStop() { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> loopControl -> onStop -> Entering function."); } + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> loopControl -> onStop -> Stopping the Loop Gracefully. See you next time!"); } + + if (global.FULL_LOG === 'true') { + logger.persist(); + } + + callBackFunction(global.DEFAULT_OK_RESPONSE); + return; + + } + + function onContinue() { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> loopControl -> onContinue -> Entering function."); } + + /* Indicator bots are going to be executed after a configured period of time after the last execution ended. This is to avoid overlapping executions. */ + + switch (nextWaitTime) { + case 'Normal': { + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> loopControl -> Restarting Loop in " + (processConfig.normalWaitTime / 1000) + " seconds."); } + checkLoopHealthHandle = setTimeout(checkLoopHealth, processConfig.normalWaitTime * 5, bot.loopCounter); + nextLoopTimeoutHandle = setTimeout(loop, processConfig.normalWaitTime); + if (global.FULL_LOG === 'true') { + logger.persist(); + } + } + break; + case 'Retry': { + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> loopControl -> Restarting Loop in " + (processConfig.retryWaitTime / 1000) + " seconds."); } + checkLoopHealthHandle = setTimeout(checkLoopHealth, processConfig.retryWaitTime * 5, bot.loopCounter); + nextLoopTimeoutHandle = setTimeout(loop, processConfig.retryWaitTime); + logger.persist(); + } + break; + case 'Sleep': { + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> loopControl -> Restarting Loop in " + (processConfig.sleepWaitTime / 60000) + " minutes."); } + nextLoopTimeoutHandle = setTimeout(loop, processConfig.sleepWaitTime); + logger.persist(); + } + break; + case 'Coma': { + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> loopControl -> Restarting Loop in " + (processConfig.comaWaitTime / 3600000) + " hours."); } + nextLoopTimeoutHandle = setTimeout(loop, processConfig.comaWaitTime); + logger.persist(); + } + break; + } + } + } + + function checkLoopHealth(pLastLoop) { + + if (bot.enableCheckLoopHealth === false) { + + logger.write(MODULE_NAME, "[WARN] run -> loop -> checkLoopHealth -> bot.enableCheckLoopHealth = " + bot.enableCheckLoopHealth); + + return; + } // This gets disabled anytime the Main Loop is shut down by any condition. + + if (bot.loopCounter <= pLastLoop + 1) { // This means that the next loop started but also stopped executing abruptally. + + let now = new Date().valueOf(); + + if (now - bot.loopStartTime > processConfig.normalWaitTime) { + + logger.write(MODULE_NAME, "[ERROR] run -> loop -> checkLoopHealth -> Dead loop found -> pLastLoop = " + pLastLoop); + console.log((new Date().toISOString() + " [ERROR] run -> loop -> checkLoopHealth -> " + pad(bot.codeName, 20) + " " + pad(bot.process, 30) + " Loop # " + pad(Number(bot.loopCounter), 5) + " found dead. Resurrecting it now.")); + + logger.persist(); // We persist the logs of the failed execution. + clearTimeout(nextLoopTimeoutHandle); // We cancel the timeout in case the original loop was still running and schedulled to reexecute. + loop(); // We restart the loop so that the processing can continue. + + } else { + + console.log((new Date().toISOString() + " [WARN] run -> loop -> checkLoopHealth -> " + pad(bot.codeName, 20) + " " + pad(bot.process, 30) + " Loop # " + pad(Number(bot.loopCounter), 5) + " found delayed but still alive. No action taken.")); + + } + } + } + + function shallWeStop(stopCallBack, continueCallBack) { + + try { + + /* IMPORTANT: This function is exactly the same on the 3 modules. */ + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> shallWeStop -> Entering function. "); } + + let stop = false + if (process.env.STOP_GRACEFULLY !== undefined) + stop = JSON.parse(process.env.STOP_GRACEFULLY) + + if (!stop && global.SHALL_BOT_STOP === false) { + continueCallBack(); + } else { + stopCallBack(); + } + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> shallWeStop -> err.message = " + err.message); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return; + } + } + } catch (err) { + parentLogger.write(MODULE_NAME, "[ERROR] run -> loop -> err = ", err); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + } + } + } catch (err) { + parentLogger.write(MODULE_NAME, "[ERROR] run -> err = ", err); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } +}; From ea590b372283e78d7d9b875bbdceb16f19a6ec68 Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Tue, 16 Jul 2019 12:38:17 +0200 Subject: [PATCH 03/19] Assistant initialized and Trading Engine Running --- Context.js | 2 +- Root.js | 18 ++- Run.js | 2 +- TradingBotProcessMainLoop.js | 4 +- TradingEngineProcessMainLoop.js | 277 ++++++++++++++++---------------- 5 files changed, 150 insertions(+), 153 deletions(-) diff --git a/Context.js b/Context.js index 3a2dae71a..16949f4d8 100644 --- a/Context.js +++ b/Context.js @@ -74,7 +74,7 @@ return thisObject; - function initialize(pStatusDependencies, pTotalAlgobots, callBackFunction) { + function initialize(pStatusDependencies, callBackFunction) { try { diff --git a/Root.js b/Root.js index 3f85db528..4d239d6a6 100644 --- a/Root.js +++ b/Root.js @@ -236,13 +236,13 @@ // TODO Pending. Move this if inside IndicatorBotProcessMainLoop line 270 if (processConfig.framework !== undefined) { if (processConfig.framework.name === "Multi-Period-Daily" || processConfig.framework.name === "Multi-Period-Market") { - if (processConfig.startMode.noTime.beginDatetime !== undefined) { - processConfig.framework.startDate.fixedDate = processConfig.startMode.noTime.beginDatetime; - processConfig.framework.startDate.resumeExecution = false; - }else{ - processConfig.framework.startDate.resumeExecution = true; + processConfig.framework.startDate.resumeExecution = false; + if (processConfig.startMode.noTime !== undefined) { + if (processConfig.startMode.noTime.beginDatetime !== undefined) { + processConfig.framework.startDate.fixedDate = processConfig.startMode.noTime.beginDatetime; + processConfig.framework.startDate.resumeExecution = false; + } } - } } @@ -408,6 +408,10 @@ runTradingBot(botConfig, processConfig); break; } + case 'Trading-Engine': { + runTradingEngine(botConfig, processConfig); + break; + } default: { console.log(logDisplace + "Root : [ERROR] start -> findProcess -> Unexpected bot type. -> botConfig.type = " + botConfig.type); } @@ -684,7 +688,7 @@ if (err.result === global.DEFAULT_OK_RESPONSE.result) { - tradingBotMainLoop.run(pGenes, pTotalInstances, whenRunFinishes); + tradingBotMainLoop.run(pGenes, whenRunFinishes); function whenRunFinishes(err) { diff --git a/Run.js b/Run.js index b95601aa8..d24efc791 100644 --- a/Run.js +++ b/Run.js @@ -38,7 +38,7 @@ function readExecutionConfiguration() { global.DEV_TEAM = process.env.DEV_TEAM global.CURRENT_BOT_REPO = process.env.BOT + "-" + process.env.TYPE + "-Bot" - if (process.env.TYPE === 'Trading') { + if (process.env.TYPE === 'Trading' || process.env.TYPE === 'Trading-Engine') { let live = { run: "false", resumeExecution: process.env.RESUME_EXECUTION diff --git a/TradingBotProcessMainLoop.js b/TradingBotProcessMainLoop.js index 416b13e94..e4dda0911 100644 --- a/TradingBotProcessMainLoop.js +++ b/TradingBotProcessMainLoop.js @@ -77,7 +77,7 @@ } } - function run(pGenes, pTotalAlgobots, callBackFunction) { + function run(pGenes, callBackFunction) { let context; @@ -436,7 +436,7 @@ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> initializeContext -> Entering function."); } context = CONTEXT.newContext(bot, logger, UTILITIES); - context.initialize(statusDependencies, pTotalAlgobots, onInizialized); + context.initialize(statusDependencies, onInizialized); function onInizialized(err) { diff --git a/TradingEngineProcessMainLoop.js b/TradingEngineProcessMainLoop.js index d20e893b9..dcadaa4ac 100644 --- a/TradingEngineProcessMainLoop.js +++ b/TradingEngineProcessMainLoop.js @@ -123,6 +123,9 @@ /* We define here all the modules that the rest of the infraestructure, including the bots themselves can consume. */ const UTILITIES = require(ROOT_DIR + 'CloudUtilities'); + const EXCHANGE_API = require('@superalgos/exchange-gateway'); + const CONTEXT = require(ROOT_DIR + 'Context'); + const ASSISTANT = require(ROOT_DIR + 'Assistant'); const STATUS_REPORT = require(ROOT_DIR + 'StatusReport'); const STATUS_DEPENDENCIES = require(ROOT_DIR + 'StatusDependencies'); const DATA_DEPENDENCIES = require(ROOT_DIR + 'DataDependencies'); @@ -242,36 +245,7 @@ switch (err.result) { case global.DEFAULT_OK_RESPONSE.result: { if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> initializeDataDependencies -> onInizialized -> Execution finished well."); } - - /* If the process is configured to run inside a framework, we continue there, otherwise we run the bot directly. */ - if (processConfig.framework === undefined) { - initializeUserBot(); - return; - } - - switch (processConfig.framework.name) { - case 'Multi-Period-Market': { - processFramework = MULTI_PERIOD_MARKET.newMultiPeriodMarket(bot, logger, COMMONS_MODULE, UTILITIES, USER_BOT_MODULE, COMMONS_MODULE); - intitializeProcessFramework(); - break; - } - case 'Multi-Period-Daily': { - processFramework = MULTI_PERIOD_DAILY.newMultiPeriodDaily(bot, logger, COMMONS_MODULE, UTILITIES, USER_BOT_MODULE, COMMONS_MODULE); - intitializeProcessFramework(); - break; - } - default: { - logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeDataDependencies -> onInizialized -> Process Framework not Supported."); - logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeDataDependencies -> onInizialized -> Process Framework Name = " + processConfig.framework.name); - logger.persist(); - clearInterval(fixedTimeLoopIntervalHandle); - clearTimeout(nextLoopTimeoutHandle); - clearTimeout(checkLoopHealthHandle); - bot.enableCheckLoopHealth = false; - callBackFunction(err); - return; - } - } + initializeContext(); return; } case global.DEFAULT_RETRY_RESPONSE.result: { // Something bad happened, but if we retry in a while it might go through the next time. @@ -326,36 +300,35 @@ } } - function initializeUserBot() { + function initializeContext() { try { - if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> initializeUserBot -> Entering function."); } + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> initializeContext -> Entering function."); } - usertBot = USER_BOT_MODULE.newUserBot(bot, logger, COMMONS_MODULE, UTILITIES, fileStorage); - - usertBot.initialize(statusDependencies, undefined, undefined, onInizialized); + context = CONTEXT.newContext(bot, logger, UTILITIES); + context.initialize(statusDependencies, onInizialized); function onInizialized(err) { try { - if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> initializeUserBot -> onInizialized -> Entering function."); } + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> initializeContext -> onInizialized -> Entering function."); } switch (err.result) { case global.DEFAULT_OK_RESPONSE.result: { - logger.write(MODULE_NAME, "[INFO] run -> loop -> initializeUserBot -> onInizialized -> Execution finished well."); - startUserBot(); + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> initializeContext -> onInizialized -> Execution finished well."); } + initializeExchangeAPI(); return; } case global.DEFAULT_RETRY_RESPONSE.result: { // Something bad happened, but if we retry in a while it might go through the next time. - logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeUserBot -> onInizialized -> Retry Later. Requesting Execution Retry."); + logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeContext -> onInizialized -> Retry Later. Requesting Execution Retry."); nextWaitTime = 'Retry'; loopControl(nextWaitTime); return; } case global.DEFAULT_FAIL_RESPONSE.result: { // This is an unexpected exception that we do not know how to handle. - logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeUserBot -> onInizialized -> Operation Failed. Aborting the process."); + logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeContext -> onInizialized -> Operation Failed. Aborting the process."); logger.persist(); clearInterval(fixedTimeLoopIntervalHandle); clearTimeout(nextLoopTimeoutHandle); @@ -364,36 +337,9 @@ callBackFunction(err); return; } - case global.CUSTOM_OK_RESPONSE.result: { - - switch (err.message) { - case "Too far in the future.": { - logger.write(MODULE_NAME, "[WARN] run -> loop -> initializeUserBot -> onInizialized > Too far in the future. This Loop will enter in coma."); - nextWaitTime = 'Coma'; - loopControl(nextWaitTime); - return; - } - case "Not needed now, but soon.": { - logger.write(MODULE_NAME, "[WARN] run -> loop -> initializeUserBot -> onInizialized > Not needed now, but soon. This Loop will continue with Normal wait time."); - nextWaitTime = 'Normal'; - loopControl(nextWaitTime); - return; - } - default: { - logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeUserBot -> onInizialized > Unhandled custom response received. -> err.message = " + err.message); - logger.persist(); - clearInterval(fixedTimeLoopIntervalHandle); - clearTimeout(nextLoopTimeoutHandle); - clearTimeout(checkLoopHealthHandle); - bot.enableCheckLoopHealth = false; - callBackFunction(err); - return; - } - } - } default: { - logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeUserBot -> onInizialized -> Unhandled err.result received. -> err.result = " + err.result); - logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeUserBot -> onInizialized -> Unhandled err.result received. -> err.message = " + err.message); + logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeContext -> onInizialized -> Unhandled err.result received. -> err.result = " + err.result); + logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeContext -> onInizialized -> Unhandled err.result received. -> err.message = " + err.message); logger.persist(); clearInterval(fixedTimeLoopIntervalHandle); @@ -406,7 +352,7 @@ } } catch (err) { - logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeUserBot -> onInizialized -> err = ", err); + logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeContext -> onInizialized -> err = ", err); logger.persist(); clearInterval(fixedTimeLoopIntervalHandle); clearTimeout(nextLoopTimeoutHandle); @@ -417,7 +363,7 @@ } } catch (err) { - logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeUserBot -> err = ", err); + logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeContext -> err = ", err); logger.persist(); clearInterval(fixedTimeLoopIntervalHandle); clearTimeout(nextLoopTimeoutHandle); @@ -427,35 +373,36 @@ } } - function startUserBot() { + function initializeExchangeAPI() { try { - if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> startUserBot -> Entering function."); } + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> initializeExchangeAPI -> Entering function."); } - usertBot.start(onFinished); + exchangeAPI = EXCHANGE_API.newExchangeAPI(logger, global.EXCHANGE_NAME); - function onFinished(err) { + exchangeAPI.initialize(onInizialized); + + function onInizialized(err) { try { - if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> startUserBot -> onFinished -> Entering function."); } + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> initializeContext -> onInizialized -> onInizialized -> Entering function."); } switch (err.result) { case global.DEFAULT_OK_RESPONSE.result: { - logger.write(MODULE_NAME, "[INFO] run -> loop -> startUserBot -> onFinished -> Execution finished well."); - nextWaitTime = 'Normal'; - loopControl(nextWaitTime); + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> initializeExchangeAPI -> onInizialized -> Execution finished well."); } + initializeAssistant(); return; } case global.DEFAULT_RETRY_RESPONSE.result: { // Something bad happened, but if we retry in a while it might go through the next time. - logger.write(MODULE_NAME, "[ERROR] run -> loop -> startUserBot -> onFinished -> Retry Later. Requesting Execution Retry."); + logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeExchangeAPI -> onInizialized -> Retry Later. Requesting Execution Retry."); nextWaitTime = 'Retry'; loopControl(nextWaitTime); return; } case global.DEFAULT_FAIL_RESPONSE.result: { // This is an unexpected exception that we do not know how to handle. - logger.write(MODULE_NAME, "[ERROR] run -> loop -> startUserBot -> onFinished -> Operation Failed. Aborting the process."); + logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeExchangeAPI -> onInizialized -> Operation Failed. Aborting the process."); logger.persist(); clearInterval(fixedTimeLoopIntervalHandle); clearTimeout(nextLoopTimeoutHandle); @@ -464,66 +411,9 @@ callBackFunction(err); return; } - case global.CUSTOM_OK_RESPONSE.result: { - - switch (err.message) { - case "Dependency does not exist.": { - logger.write(MODULE_NAME, "[WARN] run -> loop -> startUserBot -> onFinished -> Dependency does not exist. This Loop will go to sleep."); - nextWaitTime = 'Sleep'; - loopControl(nextWaitTime); - return; - } - case "Dependency not ready.": { - logger.write(MODULE_NAME, "[WARN] run -> loop -> startUserBot -> onFinished -> Dependency not ready. This Loop will go to sleep."); - nextWaitTime = 'Sleep'; - loopControl(nextWaitTime); - return; - } - case "Month before it is needed.": { - logger.write(MODULE_NAME, "[WARN] run -> loop -> startUserBot -> onFinished -> Month before it is needed. This Loop will be terminated."); - logger.persist(); - clearInterval(fixedTimeLoopIntervalHandle); - clearTimeout(nextLoopTimeoutHandle); - clearTimeout(checkLoopHealthHandle); - bot.enableCheckLoopHealth = false; - callBackFunction(global.DEFAULT_OK_RESPONSE); - return; - } - case "Month fully processed.": { - logger.write(MODULE_NAME, "[WARN] run -> loop -> startUserBot -> onFinished -> Month fully processed. This Loop will be terminated."); - logger.persist(); - clearInterval(fixedTimeLoopIntervalHandle); - clearTimeout(nextLoopTimeoutHandle); - clearTimeout(checkLoopHealthHandle); - bot.enableCheckLoopHealth = false; - callBackFunction(global.DEFAULT_OK_RESPONSE); - return; - } - case "End of the month reached.": { - logger.write(MODULE_NAME, "[WARN] run -> loop -> startUserBot -> onFinished -> End of the month reached. This Loop will be terminated."); - logger.persist(); - clearInterval(fixedTimeLoopIntervalHandle); - clearTimeout(nextLoopTimeoutHandle); - clearTimeout(checkLoopHealthHandle); - bot.enableCheckLoopHealth = false; - callBackFunction(global.DEFAULT_OK_RESPONSE); - return; - } - default: { - logger.write(MODULE_NAME, "[ERROR] run -> loop -> startUserBot -> onFinished -> Unhandled custom response received. -> err.message = " + err.message); - logger.persist(); - clearInterval(fixedTimeLoopIntervalHandle); - clearTimeout(nextLoopTimeoutHandle); - clearTimeout(checkLoopHealthHandle); - bot.enableCheckLoopHealth = false; - callBackFunction(err); - return; - } - } - } default: { - logger.write(MODULE_NAME, "[ERROR] run -> loop -> startUserBot -> onFinished -> Unhandled err.result received. -> err.result = " + err.result); - logger.write(MODULE_NAME, "[ERROR] run -> loop -> startUserBot -> onFinished -> Unhandled err.result received. -> err.message = " + err.message); + logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeExchangeAPI -> onInizialized -> Unhandled err.result received. -> err.result = " + err.result); + logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeExchangeAPI -> onInizialized -> Unhandled err.result received. -> err.message = " + err.message); logger.persist(); clearInterval(fixedTimeLoopIntervalHandle); @@ -536,7 +426,7 @@ } } catch (err) { - logger.write(MODULE_NAME, "[ERROR] run -> loop -> startUserBot -> onFinished -> err = ", err); + logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeContext -> onInizialized -> onInizialized -> err = ", err); logger.persist(); clearInterval(fixedTimeLoopIntervalHandle); clearTimeout(nextLoopTimeoutHandle); @@ -547,7 +437,109 @@ } } catch (err) { - logger.write(MODULE_NAME, "[ERROR] run -> loop -> startUserBot -> err = ", err); + logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeExchangeAPI -> err = ", err); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + } + } + + function initializeAssistant() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> initializeAssistant -> Entering function."); } + + assistant = ASSISTANT.newAssistant(bot, logger, UTILITIES); + assistant.initialize(context, exchangeAPI, dataDependencies, onInizialized); + + function onInizialized(err) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> initializeAssistant -> onInizialized -> Entering function."); } + + switch (err.result) { + case global.DEFAULT_OK_RESPONSE.result: { + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> initializeAssistant -> onInizialized -> Execution finished well."); } + + /* If the process is configured to run inside a framework, we continue there, otherwise we run the bot directly. */ + if (processConfig.framework === undefined) { + initializeUserBot(); + return; + } + + switch (processConfig.framework.name) { + case 'Multi-Period-Market': { + processFramework = MULTI_PERIOD_MARKET.newMultiPeriodMarket(bot, logger, COMMONS_MODULE, UTILITIES, USER_BOT_MODULE, COMMONS_MODULE); + intitializeProcessFramework(); + break; + } + case 'Multi-Period-Daily': { + processFramework = MULTI_PERIOD_DAILY.newMultiPeriodDaily(bot, logger, COMMONS_MODULE, UTILITIES, USER_BOT_MODULE, COMMONS_MODULE); + intitializeProcessFramework(); + break; + } + default: { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeAssistant -> onInizialized -> Process Framework not Supported."); + logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeAssistant -> onInizialized -> Process Framework Name = " + processConfig.framework.name); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + return; + } + } + return; + } + case global.DEFAULT_RETRY_RESPONSE.result: { // Something bad happened, but if we retry in a while it might go through the next time. + logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeAssistant -> onInizialized -> Retry Later. Requesting Execution Retry."); + nextWaitTime = 'Retry'; + loopControl(nextWaitTime); + return; + } + case global.DEFAULT_FAIL_RESPONSE.result: { // This is an unexpected exception that we do not know how to handle. + logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeAssistant -> onInizialized -> Operation Failed. Aborting the process."); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + return; + } + default: { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeAssistant -> onInizialized -> Unhandled err.result received. -> err.result = " + err.result); + logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeAssistant -> onInizialized -> Unhandled err.result received. -> err.message = " + err.message); + + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return; + } + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeAssistant -> onInizialized -> err = ", err); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + } + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeAssistant -> err = ", err); logger.persist(); clearInterval(fixedTimeLoopIntervalHandle); clearTimeout(nextLoopTimeoutHandle); @@ -907,6 +899,7 @@ return; } } + } catch (err) { parentLogger.write(MODULE_NAME, "[ERROR] run -> loop -> err = ", err); clearInterval(fixedTimeLoopIntervalHandle); From ec2b880b123951961991d5476bc45b46cbf077ab Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Tue, 16 Jul 2019 14:13:45 +0200 Subject: [PATCH 04/19] Assistant providing ticker at the simulation!! --- IndicatorBotProcessMainLoop.js | 2 +- MultiPeriodDaily.js | 4 ++-- MultiPeriodMarket.js | 4 ++-- TradingEngineProcessMainLoop.js | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/IndicatorBotProcessMainLoop.js b/IndicatorBotProcessMainLoop.js index d322089bb..cc1e4bcc4 100644 --- a/IndicatorBotProcessMainLoop.js +++ b/IndicatorBotProcessMainLoop.js @@ -563,7 +563,7 @@ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> intitializeProcessFramework -> Entering function."); } - processFramework.initialize(processConfig, statusDependencies, dataDependencies, onInizialized, dataDependencies); + processFramework.initialize(processConfig, statusDependencies, dataDependencies, undefined, onInizialized); function onInizialized(err) { diff --git a/MultiPeriodDaily.js b/MultiPeriodDaily.js index 3db0e3d44..06e4edcd3 100644 --- a/MultiPeriodDaily.js +++ b/MultiPeriodDaily.js @@ -27,7 +27,7 @@ return thisObject; - function initialize(pProcessConfig, pStatusDependencies, pDataDependencies, callBackFunction) { + function initialize(pProcessConfig, pStatusDependencies, pDataDependencies, pAssistant, callBackFunction) { try { @@ -59,7 +59,7 @@ } usertBot = USER_BOT_MODULE.newUserBot(bot, logger, COMMONS_MODULE, UTILITIES, fileStorage); - usertBot.initialize(dataDependencies, callBackFunction); + usertBot.initialize(dataDependencies, callBackFunction, pAssistant); } catch (err) { logger.write(MODULE_NAME, "[ERROR] initialize -> err = ", err); diff --git a/MultiPeriodMarket.js b/MultiPeriodMarket.js index 2990cbc56..d5bf944ee 100644 --- a/MultiPeriodMarket.js +++ b/MultiPeriodMarket.js @@ -23,7 +23,7 @@ return thisObject; - function initialize(pProcessConfig, pStatusDependencies, pDataDependencies, callBackFunction) { + function initialize(pProcessConfig, pStatusDependencies, pDataDependencies, pAssistant, callBackFunction) { try { @@ -55,7 +55,7 @@ } usertBot = USER_BOT_MODULE.newUserBot(bot, logger, COMMONS_MODULE, UTILITIES, fileStorage); - usertBot.initialize(dataDependencies, callBackFunction); + usertBot.initialize(dataDependencies, callBackFunction, pAssistant); } catch (err) { logger.write(MODULE_NAME, "[ERROR] initialize -> err = ", err); diff --git a/TradingEngineProcessMainLoop.js b/TradingEngineProcessMainLoop.js index dcadaa4ac..c18976064 100644 --- a/TradingEngineProcessMainLoop.js +++ b/TradingEngineProcessMainLoop.js @@ -555,7 +555,7 @@ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> intitializeProcessFramework -> Entering function."); } - processFramework.initialize(processConfig, statusDependencies, dataDependencies, onInizialized, dataDependencies); + processFramework.initialize(processConfig, statusDependencies, dataDependencies, assistant, onInizialized); function onInizialized(err) { From 8d33a3f143de5823a7ee6df58e180187a023c3d5 Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Mon, 22 Jul 2019 23:01:11 +0200 Subject: [PATCH 05/19] Trading Engine Main Loop --- MultiPeriodDaily.js | 22 ++++----- Root.js | 6 +++ Run.js | 4 +- TradingEngineProcessMainLoop.js | 79 ++++++++++++++++++++++++++++++++- 4 files changed, 98 insertions(+), 13 deletions(-) diff --git a/MultiPeriodDaily.js b/MultiPeriodDaily.js index 06e4edcd3..c8e9b7590 100644 --- a/MultiPeriodDaily.js +++ b/MultiPeriodDaily.js @@ -284,7 +284,7 @@ let n; let botNeverRan = true; - bot.processDatetime = new Date(contextVariables.lastFile.valueOf() - ONE_DAY_IN_MILISECONDS); // Go back one day to start well when we advance time at the begining of the loop. + bot.multiPeriodDailyProcessDatetime = new Date(contextVariables.lastFile.valueOf() - ONE_DAY_IN_MILISECONDS); // Go back one day to start well when we advance time at the begining of the loop. advanceTime(); @@ -294,21 +294,21 @@ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> processTimePeriods -> advanceTime -> Entering function."); } - bot.processDatetime = new Date(bot.processDatetime.valueOf() + ONE_DAY_IN_MILISECONDS); - previousDay = new Date(bot.processDatetime.valueOf() - ONE_DAY_IN_MILISECONDS); + bot.multiPeriodDailyProcessDatetime = new Date(bot.multiPeriodDailyProcessDatetime.valueOf() + ONE_DAY_IN_MILISECONDS); + previousDay = new Date(bot.multiPeriodDailyProcessDatetime.valueOf() - ONE_DAY_IN_MILISECONDS); - console.log(new Date().toISOString() + " " + pad(bot.codeName, 20) + " " + pad(bot.process, 30) + " " + " bot.processDatetime = " + bot.processDatetime.toISOString()); + console.log(new Date().toISOString() + " " + pad(bot.codeName, 20) + " " + pad(bot.process, 30) + " " + " bot.multiPeriodDailyProcessDatetime = " + bot.multiPeriodDailyProcessDatetime.toISOString()); if(global.FULL_LOG === 'true'){ logger.newInternalLoop(bot.codeName, bot.process); } - if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> processTimePeriods -> advanceTime -> bot.processDatetime = " + bot.processDatetime); } + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> processTimePeriods -> advanceTime -> bot.multiPeriodDailyProcessDatetime = " + bot.multiPeriodDailyProcessDatetime); } if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> processTimePeriods -> advanceTime -> previousDay = " + previousDay); } /* Validation that we are not going past the head of the market. */ - if (bot.processDatetime.valueOf() > contextVariables.dateEndOfMarket.valueOf()) { + if (bot.multiPeriodDailyProcessDatetime.valueOf() > contextVariables.dateEndOfMarket.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 -> processTimePeriods -> advanceTime -> " + logText); } @@ -395,7 +395,7 @@ let previousFile; let currentFile; - if (bot.processDatetime.valueOf() > contextVariables.dateBeginOfMarket.valueOf()) { + if (bot.multiPeriodDailyProcessDatetime.valueOf() > contextVariables.dateBeginOfMarket.valueOf()) { getPreviousFile(); } else { previousFile = []; @@ -473,7 +473,7 @@ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> processTimePeriods -> periodsLoopBody -> dependencyLoopBody -> getCurrentFile -> Entering function."); } - let dateForPath = bot.processDatetime.getUTCFullYear() + '/' + utilities.pad(bot.processDatetime.getUTCMonth() + 1, 2) + '/' + utilities.pad(bot.processDatetime.getUTCDate(), 2); + let dateForPath = bot.multiPeriodDailyProcessDatetime.getUTCFullYear() + '/' + utilities.pad(bot.multiPeriodDailyProcessDatetime.getUTCMonth() + 1, 2) + '/' + utilities.pad(bot.multiPeriodDailyProcessDatetime.getUTCDate(), 2); let filePath = dependency.product + '/' + "Multi-Period-Daily" + "/" + outputPeriodLabel + "/" + dateForPath; let fileName = market.assetA + '_' + market.assetB + ".json"; @@ -563,7 +563,7 @@ dataFiles, timePeriod, outputPeriodLabel, - bot.processDatetime, + bot.multiPeriodDailyProcessDatetime, contextVariables.dateBeginOfMarket, contextVariables.dateEndOfMarket, interExecutionMemoryArray[n], @@ -632,7 +632,7 @@ return; } - writeStatusReport(bot.processDatetime, advanceTime); + writeStatusReport(bot.multiPeriodDailyProcessDatetime, advanceTime); } catch (err) { logger.write(MODULE_NAME, "[ERROR] start -> processTimePeriods -> controlLoop -> onWritten -> err = ", err); @@ -667,7 +667,7 @@ let folderName = bot.products[productIndex].codeName; - writeDataRange(contextVariables.dateBeginOfMarket, bot.processDatetime, folderName, controlLoop); + writeDataRange(contextVariables.dateBeginOfMarket, bot.multiPeriodDailyProcessDatetime, folderName, controlLoop); } function controlLoop() { diff --git a/Root.js b/Root.js index 4d239d6a6..1c230da11 100644 --- a/Root.js +++ b/Root.js @@ -243,6 +243,12 @@ processConfig.framework.startDate.resumeExecution = false; } } + if (processConfig.startMode.live !== undefined) { + if (processConfig.startMode.live.beginDatetime !== undefined) { + processConfig.framework.startDate.fixedDate = processConfig.startMode.live.beginDatetime; + processConfig.framework.startDate.resumeExecution = false; + } + } } } diff --git a/Run.js b/Run.js index d24efc791..bbbb3cb21 100644 --- a/Run.js +++ b/Run.js @@ -41,7 +41,9 @@ function readExecutionConfiguration() { if (process.env.TYPE === 'Trading' || process.env.TYPE === 'Trading-Engine') { let live = { run: "false", - resumeExecution: process.env.RESUME_EXECUTION + resumeExecution: process.env.RESUME_EXECUTION, + beginDatetime: process.env.BEGIN_DATE_TIME, + endDatetime: process.env.END_DATE_TIME } let backtest = { diff --git a/TradingEngineProcessMainLoop.js b/TradingEngineProcessMainLoop.js index c18976064..a156cf1c1 100644 --- a/TradingEngineProcessMainLoop.js +++ b/TradingEngineProcessMainLoop.js @@ -666,7 +666,7 @@ case global.DEFAULT_OK_RESPONSE.result: { logger.write(MODULE_NAME, "[INFO] run -> loop -> startProcessFramework -> onFinished -> Execution finished well."); nextWaitTime = 'Normal'; - loopControl(nextWaitTime); + saveContext(); return; } case global.DEFAULT_RETRY_RESPONSE.result: { // Something bad happened, but if we retry in a while it might go through the next time. @@ -778,6 +778,83 @@ } } + function saveContext() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> saveContext -> Entering function."); } + + context.saveThemAll(onFinished); + + function onFinished(err) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> saveContext -> onFinished -> Entering function."); } + + switch (err.result) { + case global.DEFAULT_OK_RESPONSE.result: { + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> saveContext -> onFinished -> Execution finished well."); } + nextWaitTime = 'Normal'; + loopControl(nextWaitTime); + return; + } + case global.DEFAULT_RETRY_RESPONSE.result: { // Something bad happened, but if we retry in a while it might go through the next time. + logger.write(MODULE_NAME, "[ERROR] run -> loop -> saveContext -> onFinished -> Can not retry at this point."); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return; + } + case global.DEFAULT_FAIL_RESPONSE.result: { // This is an unexpected exception that we do not know how to handle. + logger.write(MODULE_NAME, "[ERROR] run -> loop -> saveContext -> onFinished -> Operation Failed. Aborting the process."); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + return; + } + default: { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> saveContext -> onFinished -> Unhandled err.result received. -> err.result = " + err.result); + logger.write(MODULE_NAME, "[ERROR] run -> loop -> saveContext -> onFinished -> Unhandled err.result received. -> err.message = " + err.message); + + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return; + } + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> saveContext -> onFinished -> err = ", err); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + } + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> saveContext -> err = ", err); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + } + } + function loopControl(nextWaitTime) { if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> loopControl -> Entering function."); } From e45d58ba312fb0e2473ecf38ba2e34abd89c8e59 Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Tue, 30 Jul 2019 10:47:38 +0200 Subject: [PATCH 06/19] Renaming .ENV Variables --- Integrations/Strategy.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Integrations/Strategy.js b/Integrations/Strategy.js index 033e29c53..ede929bbe 100644 --- a/Integrations/Strategy.js +++ b/Integrations/Strategy.js @@ -37,7 +37,7 @@ exports.getStrategy = async function () { global.STRATEGY = strategizerResponse.data.data.strategizer_TradingSystemByFb.data; } else { - let fileLocation = process.env.CONFIG_PATH + 'userConfig.json' + let fileLocation = process.env.INTER_PROCESS_FILES_PATH + 'definition.json' let strategy = await readFileAsync(fileLocation, { encoding: 'utf8' }) global.STRATEGY = JSON.parse(strategy) } From 0fbee463c8f247f5dfba7511c0e613b775dfe7a9 Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Tue, 30 Jul 2019 20:28:30 +0200 Subject: [PATCH 07/19] path adjustments --- Integrations/Ecosystem.js | 2 +- Integrations/Strategy.js | 2 +- Project.njsproj | 25 ++----------------------- 3 files changed, 4 insertions(+), 25 deletions(-) diff --git a/Integrations/Ecosystem.js b/Integrations/Ecosystem.js index 7d8832b02..fcf1fd364 100644 --- a/Integrations/Ecosystem.js +++ b/Integrations/Ecosystem.js @@ -74,7 +74,7 @@ exports.getEcosystem = async function () { global.ECOSYSTEM = ecosystemResponse.data.data.web_GetEcosystem } else { - let fileLocation = process.env.CONFIG_PATH + 'ecosystem.json' + let fileLocation = process.env.INTER_PROCESS_FILES_PATH + '/ecosystem.json' let ecosystem = await readFileAsync(fileLocation, { encoding: 'utf8' }) global.ECOSYSTEM = JSON.parse(ecosystem) } diff --git a/Integrations/Strategy.js b/Integrations/Strategy.js index ede929bbe..de41b75c5 100644 --- a/Integrations/Strategy.js +++ b/Integrations/Strategy.js @@ -37,7 +37,7 @@ exports.getStrategy = async function () { global.STRATEGY = strategizerResponse.data.data.strategizer_TradingSystemByFb.data; } else { - let fileLocation = process.env.INTER_PROCESS_FILES_PATH + 'definition.json' + let fileLocation = process.env.INTER_PROCESS_FILES_PATH + '/definition.json' let strategy = await readFileAsync(fileLocation, { encoding: 'utf8' }) global.STRATEGY = JSON.parse(strategy) } diff --git a/Project.njsproj b/Project.njsproj index b286cdfa9..a23e91de9 100644 --- a/Project.njsproj +++ b/Project.njsproj @@ -24,35 +24,18 @@ Code - - Code - - - Code - - - Code - + - - Code - Code Code - - Code - Code - - Code - Code @@ -62,9 +45,6 @@ Code - - Code - Code @@ -85,15 +65,14 @@ Code - Code + - From 7b79ad804fc282743725156ffae8af52620735bc Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Wed, 31 Jul 2019 09:02:51 +0200 Subject: [PATCH 08/19] Returning Error from Exchange Gateway --- Assistant.js | 2 +- package-lock.json | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Assistant.js b/Assistant.js index 23b0b707e..b69ac41a9 100644 --- a/Assistant.js +++ b/Assistant.js @@ -1067,7 +1067,7 @@ break; case global.DEFAULT_FAIL_RESPONSE.result: { // This is an unexpected exception that we do not know how to handle. logger.write(MODULE_NAME, "[ERROR] putPosition -> onResponse -> Operation Failed. Aborting the process."); - callBackFunction(global.DEFAULT_FAIL_RESPONSE); + callBackFunction(err); return; } break; diff --git a/package-lock.json b/package-lock.json index 1f87f54e7..b2391e70d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,12 +11,12 @@ "requires": { "axios": "0.19.0", "dotenv": "8.0.0", - "nonce": "^1.0.4", - "querystring": "^0.2.0", - "request": "^2.88.0", - "retry": "^0.12.0", - "superagent": "^5.0.2", - "underscore": "^1.9.1" + "nonce": "1.0.4", + "querystring": "0.2.0", + "request": "2.88.0", + "retry": "0.12.0", + "superagent": "5.1.0", + "underscore": "1.9.1" } }, "@superalgos/mqservice": { @@ -596,7 +596,7 @@ "mime": "2.4.4", "qs": "6.7.0", "readable-stream": "3.4.0", - "semver": "6.2.0" + "semver": "6.3.0" }, "dependencies": { "debug": { From 7f229f731f2631facfce37a648ac28d64486b81b Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Wed, 31 Jul 2019 14:00:22 +0200 Subject: [PATCH 09/19] Messages Improved --- Assistant.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Assistant.js b/Assistant.js index b69ac41a9..27158d58a 100644 --- a/Assistant.js +++ b/Assistant.js @@ -942,7 +942,7 @@ let err = { result: global.DEFAULT_FAIL_RESPONSE.result, - message: 'pAmountA is grater than the Available Balance.' + message: 'Not Enough Available Balance to Buy.' }; callBackFunction(err); @@ -960,7 +960,7 @@ let err = { result: global.DEFAULT_FAIL_RESPONSE.result, - message: 'pAmountB is grater than the Available Balance.' + message: 'Not Enough Available Balance to Sell.' }; callBackFunction(err); From 04228bb8afb21bda799be7db22caddb36ae689a8 Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Fri, 2 Aug 2019 10:39:01 +0200 Subject: [PATCH 10/19] Several Improvementes - Contains Provisional Simulator Code --- Assistant.js | 8 +- Commons.js | 2349 +++++++++++++++++++++++++++++++ Context.js | 16 +- Project.njsproj | 2 + Run.js | 37 +- TradingEngineProcessMainLoop.js | 6 +- User.Bot.js | 556 ++++++++ 7 files changed, 2954 insertions(+), 20 deletions(-) create mode 100644 Commons.js create mode 100644 User.Bot.js diff --git a/Assistant.js b/Assistant.js index 27158d58a..bf6168baf 100644 --- a/Assistant.js +++ b/Assistant.js @@ -102,7 +102,7 @@ if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] initialize -> getMarketRateFromExchange -> onTicker -> We could not get the Market Price now."); } - callBackFunction(err); + callBackFunction(global.DEFAULT_RETRY_RESPONSE); return; } @@ -653,11 +653,13 @@ let sumAssetA = 0; let sumAssetB = 0; + let sumRate = 0; for (let k = 0; k < pTrades.length; k++) { let trade = pTrades[k]; sumAssetA = sumAssetA + thisObject.truncDecimals(trade.amountA); sumAssetB = sumAssetB + thisObject.truncDecimals(trade.amountB); + sumRate = sumRate + thisObject.truncDecimals(trade.rate); } sumAssetA = thisObject.truncDecimals(sumAssetA); @@ -683,9 +685,9 @@ position.status = "executed"; if (position.type === "sell") { - context.newHistoryRecord.sellExecRate = position.rate; + context.newHistoryRecord.sellExecRate = sumRate / pTrades.length; } else { - context.newHistoryRecord.buyExecRate = position.rate; + context.newHistoryRecord.buyExecRate = sumRate / pTrades.length; } applyTradesToContext(pTrades); diff --git a/Commons.js b/Commons.js new file mode 100644 index 000000000..c03e873f7 --- /dev/null +++ b/Commons.js @@ -0,0 +1,2349 @@ +const strategy = require('./Integrations/Strategy') + +exports.newCommons = function newCommons(bot, logger, UTILITIES) { + + const { orderMessage } = require("@superalgos/mqservice") + + const { + MESSAGE_ENTITY, MESSAGE_TYPE, ORDER_CREATOR, ORDER_TYPE, + ORDER_OWNER, ORDER_DIRECTION, ORDER_STATUS, ORDER_EXIT_OUTCOME, + createMessage, getMessage, getExpandedMessage + } = orderMessage.newOrderMessage() + + const FULL_LOG = true; + const LOG_FILE_CONTENT = false; + + const MODULE_NAME = "Commons"; + const ONE_DAY_IN_MILISECONDS = 24 * 60 * 60 * 1000; + const GMT_SECONDS = ':00.000 GMT+0000'; + + let thisObject = { + initializeData: initializeData, + runSimulation: runSimulation, + buildLRC: buildLRC, + buildPercentageBandwidthMap: buildPercentageBandwidthMap, + buildBollingerBandsMap: buildBollingerBandsMap, + buildBollingerChannelsArray: buildBollingerChannelsArray, + buildBollingerSubChannelsArray: buildBollingerSubChannelsArray, + buildCandles: buildCandles + }; + + let utilities = UTILITIES.newCloudUtilities(bot, logger); + + let LRCMap = new Map(); + let percentageBandwidthMap = new Map(); + let bollingerBandsMap = new Map(); + let bollingerChannelsArray = []; + let bollingerSubChannelsArray = []; + + let candles = []; + + return thisObject; + + function initializeData() { + + LRCMap = new Map(); + percentageBandwidthMap = new Map(); + bollingerBandsMap = new Map(); + bollingerChannelsArray = []; + bollingerSubChannelsArray = []; + + candles = []; + } + + async function runSimulation( + timePeriod, + currentDay, + startDate, + endDate, + interExecutionMemory, + assistant, + callback, + callBackFunction) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> Entering function."); } + + let executionArray = []; + let recordsArray = []; + let conditionsArray = []; + let strategiesArray = []; + let tradesArray = []; + let lastObjectsArray = []; + + let definition = await strategy.getStrategy(); + let tradingSystem = definition.tradingSystem; + + /* Initial Default Values */ + + let initialDate = startDate; + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> initialDate = " + initialDate); } + + const DEFAULT_BASE_ASSET_BALANCE = 1 + const DEFAULT_BASE_ASSET_MINIMUN_BALANCE = 0.5 + const DEFAULT_BASE_ASSET_MAXIMUN_BALANCE = 2 + + let initialBalanceA = DEFAULT_BASE_ASSET_BALANCE + let minimumBalanceA = DEFAULT_BASE_ASSET_MINIMUN_BALANCE + let maximumBalanceA = DEFAULT_BASE_ASSET_MAXIMUN_BALANCE + let initialBalanceB = 0 + let minimumBalanceB = 0 + let maximumBalanceB = 0 + let baseAsset = 'BTC' + + /* Parameters Processing */ + + if (tradingSystem.parameters !== undefined) { + if (tradingSystem.parameters.baseAsset !== undefined) { + if (tradingSystem.parameters.baseAsset.formula !== undefined) { + let receivedParameters + try { + receivedParameters = JSON.parse(tradingSystem.parameters.baseAsset.formula.code); + + if (receivedParameters.name !== undefined) { + baseAsset = receivedParameters.name; + if (baseAsset !== 'BTC' && baseAsset !== 'USDT') { + tradingSystem.parameters.baseAsset.formula.error = baseAsset + ' is not supported. Using default: BTC.' + baseAsset = 'BTC' + } + } + + if (baseAsset === 'BTC') { + if (receivedParameters.initialBalance !== undefined) { + initialBalanceA = receivedParameters.initialBalance; + initialBalanceB = 0 + } else { + initialBalanceA = DEFAULT_BASE_ASSET_BALANCE; + initialBalanceB = 0 + } + if (receivedParameters.minimumBalance !== undefined) { + minimumBalanceA = receivedParameters.minimumBalance; + minimumBalanceB = 0 + } else { + minimumBalanceA = DEFAULT_BASE_ASSET_MINIMUN_BALANCE; + minimumBalanceB = 0 + } + if (receivedParameters.maximumBalance !== undefined) { + maximumBalanceA = receivedParameters.maximumBalance; + maximumBalanceB = 0 + } else { + maximumBalanceA = DEFAULT_BASE_ASSET_MAXIMUN_BALANCE; + maximumBalanceB = 0 + } + } else { + if (receivedParameters.initialBalance !== undefined) { + initialBalanceB = receivedParameters.initialBalance; + initialBalanceA = 0 + } else { + initialBalanceB = DEFAULT_BASE_ASSET_BALANCE; + initialBalanceA = 0 + } + if (receivedParameters.minimumBalance !== undefined) { + minimumBalanceB = receivedParameters.minimumBalance; + minimumBalanceA = 0 + } else { + minimumBalanceB = DEFAULT_BASE_ASSET_MINIMUN_BALANCE; + minimumBalanceA = 0 + } + if (receivedParameters.maximumBalance !== undefined) { + maximumBalanceB = receivedParameters.maximumBalance; + maximumBalanceA = 0 + } else { + maximumBalanceB = DEFAULT_BASE_ASSET_MAXIMUN_BALANCE; + maximumBalanceA = 0 + } + } + } catch (err) { + tradingSystem.parameters.baseAsset.formula.error = err.message + } + } + } + } + + /* Strategy and Phases */ + + let currentStrategyIndex = -1; + let strategyStage = 'No Stage'; + + /* Stop Loss Management */ + + const MIN_STOP_LOSS_VALUE = 1 // We can not let the stop be zero to avoid division by 0 error or infinity numbers as a result. + const MAX_STOP_LOSS_VALUE = Number.MAX_SAFE_INTEGER + let stopLoss = 0; + let stopLossPhase = 0; + let stopLossStage = 'No Stage'; + + /* Take Profit Management */ + + const MIN_TAKE_PROFIT_VALUE = 1 // We can not let the buy order be zero to avoid division by 0 error or infinity numbers as a result. + const MAX_TAKE_PROFIT_VALUE = Number.MAX_SAFE_INTEGER + let previousTakeProfit = 0; + let takeProfit = 0; + let takeProfitPhase = 0; + let takeProfitStage = 'No Stage'; + + /* Simulation Records */ + + let tradePositionRate = 0; + let tradePositionSize = 0; + let positionInstant; + + let previousBalanceAssetA = 0; + let previousBalanceAssetB = 0; + let hitRatio = 0; + let ROI = 0; + let days = 0; + let anualizedRateOfReturn = 0; + let type = '""'; + let marketRate = 0; + let takePositionNow = false + + /* Assistant Info */ + + let ticker = assistant.getTicker(); + + /* In some cases we need to know if we are positioned at the last candle of the calendar day, for that we need thse variables. */ + + let lastInstantOfTheDay = currentDay.valueOf() + ONE_DAY_IN_MILISECONDS - 1; + let lastCandle = candles[candles.length - 1]; + + /* These 2 objects will allow us to create separate files for each one of them. */ + + let currentStrategy = { + begin: 0, + end: 0, + status: 0, + number: 0 + } + + let currentTrade = { + begin: 0, + end: 0, + status: 0, + profit: 0, + exitType: 0, + beginRate: 0, + endRate: 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 + day and these counters are meant to be kept along the whole market. + + To overcome this problem, we use the interExecutionMemory to record the values of the current execution + when finish. But there are a few details: + + 1. When the process is at the head of the market, it executes multple times at the same day. + 2. The same code serves execution from Market Files. + 3. In Daily Files we are receiving candles from the current day and previous day, so we need to take care of + not adding to the counters duplicate info when processing the day before candles. + + To overcome these challenges we record the values of the counters and variables on the interExecutionMemory only when + the day is complete and if we have a currentDay. That menas that for Market Files we will never use + interExecutionMemory. + */ + + let balanceAssetA = initialBalanceA; + let balanceAssetB = initialBalanceB; + + let lastTradeProfitLoss = 0; + let profit = 0; + let lastTradeROI = 0; + + let roundtrips = 0; + let fails = 0; + let hits = 0; + let periods = 0; + + /* Message to the Simulation Executor */ + + let orderId = 0; + let messageId = 0; + + let yesterday = {}; + + yesterday.balanceAssetA = balanceAssetA; + yesterday.balanceAssetB = balanceAssetB; + + yesterday.lastTradeProfitLoss = 0; + yesterday.profit = 0; + yesterday.lastTradeROI = 0; + + yesterday.Roundtrips = 0; + yesterday.fails = 0; + yesterday.hits = 0; + yesterday.Periods = 0; + + yesterday.orderId = 0; + yesterday.messageId = 0; + + yesterday.hitRatio = 0; + yesterday.ROI = 0; + yesterday.anualizedRateOfReturn = 0; + + if (interExecutionMemory.roundtrips === undefined) { + + /* Initialize the data structure we will use inter execution. */ + + interExecutionMemory.balanceAssetA = balanceAssetA; + interExecutionMemory.balanceAssetB = balanceAssetB; + + interExecutionMemory.lastTradeProfitLoss = lastTradeProfitLoss; + interExecutionMemory.profit = profit; + interExecutionMemory.lastTradeROI = lastTradeROI; + + interExecutionMemory.roundtrips = 0; + interExecutionMemory.fails = 0; + interExecutionMemory.hits = 0; + interExecutionMemory.periods = 0; + + interExecutionMemory.orderId = 0; + interExecutionMemory.messageId = 0; + + interExecutionMemory.hitRatio = 0; + interExecutionMemory.ROI = 0; + interExecutionMemory.anualizedRateOfReturn = 0; + + } else { + + /* We get the initial values from the day previous to the candles we receive at the current execution */ + + if (currentDay.valueOf() >= startDate.valueOf() + ONE_DAY_IN_MILISECONDS) { // Only after the first day we start grabbing the balance from this memory. + + balanceAssetA = interExecutionMemory.balanceAssetA; + balanceAssetB = interExecutionMemory.balanceAssetB; + + yesterday.balanceAssetA = balanceAssetA; + yesterday.balanceAssetB = balanceAssetB; + + } + + lastTradeProfitLoss = interExecutionMemory.lastTradeProfitLoss; + profit = interExecutionMemory.profit; + lastTradeROI = interExecutionMemory.lastTradeROI; + + roundtrips = interExecutionMemory.roundtrips; + fails = interExecutionMemory.fails; + hits = interExecutionMemory.hits; + periods = interExecutionMemory.periods; + + orderId = interExecutionMemory.orderId; + messageId = interExecutionMemory.messageId; + + hitRatio = interExecutionMemory.hitRatio; + ROI = interExecutionMemory.ROI; + anualizedRateOfReturn = interExecutionMemory.anualizedRateOfReturn; + + yesterday.hitRatio = hitRatio; + yesterday.ROI = ROI; + yesterday.anualizedRateOfReturn = anualizedRateOfReturn; + + } + + /* Main Simulation Loop: We go thourgh all the candles at this time period. */ + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> Main Simulation Loop -> candles.length = " + candles.length); } + + let i + initializeLoop() + + function initializeLoop() { + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> initializeLoop -> Entering function."); } + i = 0 + loop() + } + + function loop() { + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> Entering function."); } + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> i = " + i); } + + let candle = candles[i]; + let percentageBandwidth = percentageBandwidthMap.get(candle.begin); + let bollingerBand = bollingerBandsMap.get(candle.begin); + let bollingerChannel = getElement(bollingerChannelsArray, candle.begin, candle.end); + let bollingerSubChannel = getElement(bollingerSubChannelsArray, candle.begin, candle.end); + + //let LRC = LRCMap.get(candle.begin); + + /* If any of the needed indicators is missing, then that period is not calculated */ + + if (candle.begin < initialDate.valueOf()) { + controlLoop(); + return + } + if (bollingerBand === undefined) { + controlLoop(); + return + } + if (percentageBandwidth === undefined) { + controlLoop(); + return + } // percentageBandwidth might start after the first few candles. + if (bollingerChannel === undefined) { + controlLoop(); + return + } + if (bollingerSubChannel === undefined) { + controlLoop(); + return + } + + //if (LRC === undefined) { controlLoop(); return} + + periods++; + days = periods * timePeriod / ONE_DAY_IN_MILISECONDS; + + if (currentDay !== undefined) { // This means that we are processing Daily Files + if (candle.end < currentDay.valueOf()) { + yesterday.Periods++; + } + + /* We skip the candle at the head of the market because i has not closed yet. */ + let candlesPerDay = ONE_DAY_IN_MILISECONDS / timePeriod + if (i === candles.length - 1) { + if ((candles.length < candlesPerDay) || (candles.length > candlesPerDay && candles.length < candlesPerDay * 2)) { + /*We are at the head of the market, thus we skip the last candle because it has not close yet. */ + controlLoop(); + return + /* Note here that in the last candle of the first day or the second day it will use an incomplete candle and partially calculated indicators. + if we skip these two periods, then there will be a hole in the file since the last period will be missing. */ + } + } + } else { // We are processing Market Files + if (i === candles.length - 1) { + controlLoop(); + return + } + } + + let lastObjects = { + candle: clone(candle), + //LRC: clone(LRC), + bollingerBand: clone(bollingerBand), + percentageBandwidth: clone(percentageBandwidth), + bollingerChannel: clone(bollingerChannel), + bollingerSubChannel: clone(bollingerSubChannel) + } + + function clone(obj) { + if (null == obj || "object" != typeof obj) return obj; + var copy = obj.constructor(); + for (var attr in obj) { + if (obj.hasOwnProperty(attr)) copy[attr] = obj[attr]; + } + return copy; + } + + lastObjects.candle.previous = undefined; + //lastObjects.LRC.previous = undefined; + lastObjects.bollingerBand.previous = undefined; + lastObjects.percentageBandwidth.previous = undefined; + lastObjects.bollingerChannel.previous = undefined; + lastObjects.bollingerSubChannel.previous = undefined; + + lastObjectsArray.push(lastObjects); + + if (lastObjectsArray.length > 5) { + lastObjectsArray.splice(0, 1); + } + + let conditions = new Map; // Here we store the conditions values that will be use in the simulator for decision making. + let formulas = new Map; + let conditionsArrayRecord = []; // These are the records that will be saved in a file for the plotter to consume. + let conditionsArrayValues = []; // Here we store the conditions values that will be written on file for the plotter. + let formulasErrors = []; // Here we store the errors produced by all phase formulas. + let formulasValues = []; // Here we store the values produced by all phase formulas. + + /* We define and evaluate all conditions to be used later during the simulation loop. */ + + conditionsArrayRecord.push(candle.begin); + conditionsArrayRecord.push(candle.end); + + evaluateConditionsAndFormulas(tradingSystem, conditions); + + function evaluateConditionsAndFormulas(tradingSystem, conditions) { + + for (let j = 0; j < tradingSystem.strategies.length; j++) { + + let strategy = tradingSystem.strategies[j]; + + let positionSize = 0 + let positionRate = 0 + + /* Continue with the rest of the formulas and conditions */ + + let triggerStage = strategy.triggerStage + + if (triggerStage !== undefined) { + + if (triggerStage.triggerOn !== undefined) { + + for (let k = 0; k < triggerStage.triggerOn.situations.length; k++) { + + let situation = triggerStage.triggerOn.situations[k]; + + for (let m = 0; m < situation.conditions.length; m++) { + + let condition = situation.conditions[m]; + let key = j + '-' + 'triggerStage' + '-' + 'triggerOn' + '-' + k + '-' + m; + + if (condition.code !== undefined) { + newCondition(key, condition.code); + } + } + } + } + + if (triggerStage.triggerOff !== undefined) { + + for (let k = 0; k < triggerStage.triggerOff.situations.length; k++) { + + let situation = triggerStage.triggerOff.situations[k]; + + for (let m = 0; m < situation.conditions.length; m++) { + + let condition = situation.conditions[m]; + let key = j + '-' + 'triggerStage' + '-' + 'triggerOff' + '-' + k + '-' + m; + + if (condition.code !== undefined) { + newCondition(key, condition.code); + } + } + } + } + + if (triggerStage.takePosition !== undefined) { + + for (let k = 0; k < triggerStage.takePosition.situations.length; k++) { + + let situation = triggerStage.takePosition.situations[k]; + + for (let m = 0; m < situation.conditions.length; m++) { + + let condition = situation.conditions[m]; + let key = j + '-' + 'triggerStage' + '-' + 'takePosition' + '-' + k + '-' + m; + + if (condition.code !== undefined) { + newCondition(key, condition.code); + } + } + } + } + } + + let openStage = strategy.openStage + + if (openStage !== undefined) { + + /* Default Values*/ + if (baseAsset === 'BTC') { + positionSize = balanceAssetA; + positionRate = candle.close; + } else { + positionSize = balanceAssetB; + positionRate = candle.close; + } + + let initialDefinition = openStage.initialDefinition + + if (initialDefinition !== undefined) { + + if (tradePositionSize !== 0) { + positionSize = tradePositionSize + } else { + if (initialDefinition.positionSize !== undefined) { + if (initialDefinition.positionSize.formula !== undefined) { + try { + positionSize = eval(initialDefinition.positionSize.formula.code); + } catch (err) { + initialDefinition.positionSize.formula.error = err.message + } + if (isNaN(positionSize)) { + if (baseAsset === 'BTC') { + positionSize = balanceAssetA; + } else { + positionSize = balanceAssetB; + } + } else { + if (baseAsset === 'BTC') { + if (positionSize > balanceAssetA) { positionSize = balanceAssetA } + } else { + if (positionSize > balanceAssetB) { positionSize = balanceAssetB } + } + } + } + } + } + + if (tradePositionRate !== 0) { + positionRate = tradePositionRate + } else { + if (initialDefinition.positionRate !== undefined) { + if (initialDefinition.positionRate.formula !== undefined) { + try { + positionRate = eval(initialDefinition.positionRate.formula.code); + } catch (err) { + initialDefinition.positionRate.formula.error = err.message + } + if (isNaN(positionRate)) { + if (baseAsset === 'BTC') { + positionRate = candle.close; + } else { + positionRate = candle.close; + } + } + } + } + } + + if (initialDefinition.stopLoss !== undefined) { + + for (let p = 0; p < initialDefinition.stopLoss.phases.length; p++) { + + let phase = initialDefinition.stopLoss.phases[p]; + + /* Evaluate Formula */ + let formulaValue + let formulaError = '' + + if (phase.formula !== undefined) { + try { + formulaValue = eval(phase.formula.code); + if (formulaValue === Infinity) { + formulaError = "Formula evaluates to Infinity." + formulaValue = MAX_STOP_LOSS_VALUE + } + } catch (err) { + if (phase.formula.code.indexOf('previous') > 0 && err.message.indexOf('of undefined') > 0) { + /* + We are not going to set an error for the casess we are using previous and the error is that the indicator is undefined. + */ + } else { + formulaError = err.message + } + } + if (isNaN(formulaValue)) { formulaValue = 0; } + if (formulaValue < MIN_STOP_LOSS_VALUE) { + formulaValue = MIN_STOP_LOSS_VALUE + } + + formulasErrors.push('"' + formulaError + '"') + formulasValues.push(formulaValue) + let key = j + '-' + 'openStage' + '-' + 'initialDefinition' + '-' + 'stopLoss' + '-' + p; + formulas.set(key, formulaValue) + } + + /* next phase event */ + let nextPhaseEvent = phase.nextPhaseEvent; + if (nextPhaseEvent !== undefined) { + + for (let k = 0; k < nextPhaseEvent.situations.length; k++) { + + let situation = nextPhaseEvent.situations[k]; + + for (let m = 0; m < situation.conditions.length; m++) { + + let condition = situation.conditions[m]; + let key = j + '-' + 'openStage' + '-' + 'initialDefinition' + '-' + 'stopLoss' + '-' + p + '-' + k + '-' + m; + + if (condition.code !== undefined) { + newCondition(key, condition.code); + } + } + } + } + } + } + + if (initialDefinition.takeProfit !== undefined) { + + for (let p = 0; p < initialDefinition.takeProfit.phases.length; p++) { + + let phase = initialDefinition.takeProfit.phases[p]; + + /* Evaluate Formula */ + let formulaValue + let formulaError = '' + + if (phase.formula !== undefined) { + try { + formulaValue = eval(phase.formula.code); + if (formulaValue === Infinity) { + formulaError = "Formula evaluates to Infinity." + formulaValue = MAX_TAKE_PROFIT_VALUE + } + } catch (err) { + if (phase.formula.code.indexOf('previous') > 0 && err.message.indexOf('of undefined') > 0) { + /* + We are not going to set an error for the casess we are using previous and the error is that the indicator is undefined. + */ + } else { + formulaError = err.message + } + } + if (isNaN(formulaValue)) { formulaValue = 0; } + if (formulaValue < MIN_TAKE_PROFIT_VALUE) { + formulaValue = MIN_TAKE_PROFIT_VALUE + } + + formulasErrors.push('"' + formulaError + '"') + formulasValues.push(formulaValue) + let key = j + '-' + 'openStage' + '-' + 'initialDefinition' + '-' + 'takeProfit' + '-' + p; + formulas.set(key, formulaValue) + } + + /* next phase event */ + let nextPhaseEvent = phase.nextPhaseEvent; + if (nextPhaseEvent !== undefined) { + + for (let k = 0; k < nextPhaseEvent.situations.length; k++) { + + let situation = nextPhaseEvent.situations[k]; + + for (let m = 0; m < situation.conditions.length; m++) { + + let condition = situation.conditions[m]; + let key = j + '-' + 'openStage' + '-' + 'initialDefinition' + '-' + 'takeProfit' + '-' + p + '-' + k + '-' + m; + + if (condition.code !== undefined) { + newCondition(key, condition.code); + } + } + } + } + } + } + } + + strategy.positionSize = positionSize + strategy.positionRate = positionRate + } + + let manageStage = strategy.manageStage + + if (manageStage !== undefined) { + + if (manageStage.stopLoss !== undefined) { + + for (let p = 0; p < manageStage.stopLoss.phases.length; p++) { + + let phase = manageStage.stopLoss.phases[p]; + + /* Evaluate Formula */ + let formulaValue + let formulaError = '' + + if (phase.formula !== undefined) { + try { + formulaValue = eval(phase.formula.code); + if (formulaValue === Infinity) { + formulaError = "Formula evaluates to Infinity." + formulaValue = MAX_STOP_LOSS_VALUE + } + } catch (err) { + if (phase.formula.code.indexOf('previous') > 0 && err.message.indexOf('of undefined') > 0) { + /* + We are not going to set an error for the casess we are using previous and the error is that the indicator is undefined. + */ + } else { + formulaError = err.message + } + } + if (isNaN(formulaValue)) { formulaValue = 0; } + if (formulaValue < MIN_STOP_LOSS_VALUE) { + formulaValue = MIN_STOP_LOSS_VALUE + } + + formulasErrors.push('"' + formulaError + '"') + formulasValues.push(formulaValue) + let key = j + '-' + 'manageStage' + '-' + 'stopLoss' + '-' + p; + formulas.set(key, formulaValue) + } + + /* next phase event */ + let nextPhaseEvent = phase.nextPhaseEvent; + if (nextPhaseEvent !== undefined) { + + for (let k = 0; k < nextPhaseEvent.situations.length; k++) { + + let situation = nextPhaseEvent.situations[k]; + + for (let m = 0; m < situation.conditions.length; m++) { + + let condition = situation.conditions[m]; + let key = j + '-' + 'manageStage' + '-' + 'stopLoss' + '-' + p + '-' + k + '-' + m; + + if (condition.code !== undefined) { + newCondition(key, condition.code); + } + } + } + } + } + } + + if (manageStage.takeProfit !== undefined) { + + for (let p = 0; p < manageStage.takeProfit.phases.length; p++) { + + let phase = manageStage.takeProfit.phases[p]; + + /* Evaluate Formula */ + let formulaValue + let formulaError = '' + + if (phase.formula !== undefined) { + try { + formulaValue = eval(phase.formula.code); + if (formulaValue === Infinity) { + formulaError = "Formula evaluates to Infinity." + formulaValue = MAX_TAKE_PROFIT_VALUE + } + } catch (err) { + if (phase.formula.code.indexOf('previous') > 0 && err.message.indexOf('of undefined') > 0) { + /* + We are not going to set an error for the casess we are using previous and the error is that the indicator is undefined. + */ + } else { + formulaError = err.message + } + } + if (isNaN(formulaValue)) { formulaValue = 0; } + if (formulaValue < MIN_TAKE_PROFIT_VALUE) { + formulaValue = MIN_TAKE_PROFIT_VALUE + } + + formulasErrors.push('"' + formulaError + '"') + formulasValues.push(formulaValue) + let key = j + '-' + 'manageStage' + '-' + 'takeProfit' + '-' + p; + formulas.set(key, formulaValue) + } + + /* next phase event */ + let nextPhaseEvent = phase.nextPhaseEvent; + if (nextPhaseEvent !== undefined) { + + for (let k = 0; k < nextPhaseEvent.situations.length; k++) { + + let situation = nextPhaseEvent.situations[k]; + + for (let m = 0; m < situation.conditions.length; m++) { + + let condition = situation.conditions[m]; + let key = j + '-' + 'manageStage' + '-' + 'takeProfit' + '-' + p + '-' + k + '-' + m; + + if (condition.code !== undefined) { + newCondition(key, condition.code); + } + } + } + } + } + } + } + + function newCondition(key, node) { + + let condition; + let error = '' + let value + + try { + value = eval(node.code); + } catch (err) { + /* + One possible error is that the conditions references a .previous that is undefined. For this + reason and others, we will simply set the value to false. + */ + value = false + + if (node.code.indexOf('previous') > 0 && err.message.indexOf('of undefined') > 0) { + /* + We are not going to set an error for the casess we are using previous and the error is that the indicator is undefined. + */ + } else { + node.error = err.message + } + } + + condition = { + key: key, + value: value + }; + + conditions.set(condition.key, condition); + + if (condition.value) { + conditionsArrayValues.push(1); + } else { + conditionsArrayValues.push(0); + } + } + } + } + + /* Trigger On Conditions */ + if ( + strategyStage === 'No Stage' && + currentStrategyIndex === -1 + ) { + let minimumBalance + let maximumBalance + let balance + + if (baseAsset === 'BTC') { + balance = balanceAssetA + minimumBalance = minimumBalanceA + maximumBalance = maximumBalanceA + } else { + balance = balanceAssetB + minimumBalance = minimumBalanceB + maximumBalance = maximumBalanceB + } + + if (balance > minimumBalance && balance < maximumBalance) { + + /* + Here we need to pick a strategy, or if there is not suitable strategy for the current + market conditions, we pass until the next period. + + To pick a new strategy we will evaluate what we call the trigger on. Once we enter + into one strategy, we will ignore market conditions for others. However there is also + a strategy trigger off which can be hit before taking a position. If hit, we would + be outside a strategy again and looking for the condition to enter all over again. + + */ + + for (let j = 0; j < tradingSystem.strategies.length; j++) { + + let strategy = tradingSystem.strategies[j]; + + let triggerStage = strategy.triggerStage + + if (triggerStage !== undefined) { + + if (triggerStage.triggerOn !== undefined) { + + for (let k = 0; k < triggerStage.triggerOn.situations.length; k++) { + + let situation = triggerStage.triggerOn.situations[k]; + let passed = true; + + for (let m = 0; m < situation.conditions.length; m++) { + + let condition = situation.conditions[m]; + let key = j + '-' + 'triggerStage' + '-' + 'triggerOn' + '-' + k + '-' + m; + + let value = false + if (conditions.get(key) !== undefined) { + value = conditions.get(key).value; + } + + if (value === false) { passed = false; } + } + + if (passed) { + + strategyStage = 'Trigger Stage'; + currentStrategyIndex = j; + currentStrategy.begin = candle.begin; + currentStrategy.beginRate = candle.min; + currentStrategy.endRate = candle.min; // In case the strategy does not get exited + break; + } + } + } + } + } + } else { + let stopRunningDate = new Date(candle.begin) + if (balance < minimumBalance) { + tradingSystem.error = "Min Balance @ " + stopRunningDate.toUTCString() + } + if (balance > maximumBalance) { + tradingSystem.error = "Max Balance @ " + stopRunningDate.toUTCString() + } + } + } + + /* Trigger Off Condition */ + if (strategyStage === 'Trigger Stage') { + + let strategy = tradingSystem.strategies[currentStrategyIndex]; + + let triggerStage = strategy.triggerStage + + if (triggerStage !== undefined) { + + if (triggerStage.triggerOff !== undefined) { + + for (let k = 0; k < triggerStage.triggerOff.situations.length; k++) { + + let situation = triggerStage.triggerOff.situations[k]; + let passed = true; + + for (let m = 0; m < situation.conditions.length; m++) { + + let condition = situation.conditions[m]; + let key = currentStrategyIndex + '-' + 'triggerStage' + '-' + 'triggerOff' + '-' + k + '-' + m; + + let value = false + if (conditions.get(key) !== undefined) { + value = conditions.get(key).value; + } + + if (value === false) { passed = false; } + } + + if (passed) { + + currentStrategy.number = currentStrategyIndex + currentStrategy.end = candle.end; + currentStrategy.endRate = candle.min; + currentStrategy.status = 1; + strategyStage = 'No Stage'; + currentStrategyIndex = -1; + break; + } + } + } + } + } + + /* Take Position Condition */ + if (strategyStage === 'Trigger Stage') { + + let strategy = tradingSystem.strategies[currentStrategyIndex]; + + let triggerStage = strategy.triggerStage + + if (triggerStage !== undefined) { + + if (triggerStage.takePosition !== undefined) { + + for (let k = 0; k < triggerStage.takePosition.situations.length; k++) { + + let situation = triggerStage.takePosition.situations[k]; + let passed = true; + + for (let m = 0; m < situation.conditions.length; m++) { + + let condition = situation.conditions[m]; + let key = currentStrategyIndex + '-' + 'triggerStage' + '-' + 'takePosition' + '-' + k + '-' + m; + + let value = false + if (conditions.get(key) !== undefined) { + value = conditions.get(key).value; + } + + if (value === false) { passed = false; } + } + + if (passed) { + + type = '"Take Position"'; + + strategyStage = 'Open Stage'; + stopLossStage = 'Open Stage'; + takeProfitStage = 'Open Stage'; + stopLossPhase = 1; + takeProfitPhase = 1; + currentTrade.begin = candle.begin; + currentTrade.beginRate = candle.close; + takePositionNow = true + break; + } + } + } + } + } + + /* Stop Loss Management */ + if ( + (strategyStage === 'Open Stage' || strategyStage === 'Manage Stage') && + takePositionNow !== true + ) { + + checkStopPhases() + calculateStopLoss(); + + } + + function checkStopPhases() { + + let strategy = tradingSystem.strategies[currentStrategyIndex]; + + let openStage = strategy.openStage + let manageStage = strategy.manageStage + let parentNode + let j = currentStrategyIndex + let stageKey + let initialDefinitionKey = '' + let p + + if (stopLossStage === 'Open Stage' && openStage !== undefined) { + if (openStage.initialDefinition !== undefined) { + if (openStage.initialDefinition.stopLoss !== undefined) { + parentNode = openStage.initialDefinition + initialDefinitionKey = '-' + 'initialDefinition' + stageKey = 'openStage' + p = stopLossPhase - 1 + } + } + } + + if (stopLossStage === 'Manage Stage' && manageStage !== undefined) { + if (manageStage.stopLoss !== undefined) { + parentNode = manageStage + stageKey = 'manageStage' + p = stopLossPhase - 2 + } + } + + let phase = parentNode.stopLoss.phases[p]; + + let nextPhaseEvent = phase.nextPhaseEvent; + if (nextPhaseEvent !== undefined) { + + for (let k = 0; k < nextPhaseEvent.situations.length; k++) { + + let situation = nextPhaseEvent.situations[k]; + let passed = true; + + for (let m = 0; m < situation.conditions.length; m++) { + + let condition = situation.conditions[m]; + let key = j + '-' + stageKey + initialDefinitionKey + '-' + 'stopLoss' + '-' + p + '-' + k + '-' + m; + + let value = false + if (conditions.get(key) !== undefined) { + value = conditions.get(key).value; + } + + if (value === false) { passed = false; } + } + + if (passed) { + + stopLossPhase++; + stopLossStage = 'Manage Stage' + if (takeProfitPhase > 1) { strategyStage = 'Manage Stage' } + return; + } + } + } + } + + function calculateStopLoss() { + + let strategy = tradingSystem.strategies[currentStrategyIndex]; + let openStage = strategy.openStage + let manageStage = strategy.manageStage + let phase + let key + + if (stopLossStage === 'Open Stage' && openStage !== undefined) { + if (openStage.initialDefinition !== undefined) { + if (openStage.initialDefinition.stopLoss !== undefined) { + phase = openStage.initialDefinition.stopLoss.phases[stopLossPhase - 1]; + key = currentStrategyIndex + '-' + 'openStage' + '-' + 'initialDefinition' + '-' + 'stopLoss' + '-' + (stopLossPhase - 1); + } + } + } + + if (stopLossStage === 'Manage Stage' && manageStage !== undefined) { + if (manageStage.stopLoss !== undefined) { + phase = manageStage.stopLoss.phases[stopLossPhase - 2]; + key = currentStrategyIndex + '-' + 'manageStage' + '-' + 'stopLoss' + '-' + (stopLossPhase - 2); + } + } + + if (phase !== undefined) { + if (phase.formula !== undefined) { + stopLoss = formulas.get(key) + } + } + } + + /* Take Profit Management */ + if ( + (strategyStage === 'Open Stage' || strategyStage === 'Manage Stage') && + takePositionNow !== true + ) { + + checkTakeProfitPhases(); + calculateTakeProfit(); + + } + + function checkTakeProfitPhases() { + + let strategy = tradingSystem.strategies[currentStrategyIndex]; + + let openStage = strategy.openStage + let manageStage = strategy.manageStage + let parentNode + let j = currentStrategyIndex + let stageKey + let initialDefinitionKey = '' + let p + + if (takeProfitStage === 'Open Stage' && openStage !== undefined) { + if (openStage.initialDefinition !== undefined) { + if (openStage.initialDefinition.takeProfit !== undefined) { + parentNode = openStage.initialDefinition + initialDefinitionKey = '-' + 'initialDefinition' + stageKey = 'openStage' + p = takeProfitPhase - 1 + } + } + } + + if (takeProfitStage === 'Manage Stage' && manageStage !== undefined) { + if (manageStage.takeProfit !== undefined) { + parentNode = manageStage + stageKey = 'manageStage' + p = takeProfitPhase - 2 + } + } + + let phase = parentNode.takeProfit.phases[p]; + + let nextPhaseEvent = phase.nextPhaseEvent; + if (nextPhaseEvent !== undefined) { + + for (let k = 0; k < nextPhaseEvent.situations.length; k++) { + + let situation = nextPhaseEvent.situations[k]; + let passed = true; + + for (let m = 0; m < situation.conditions.length; m++) { + + let condition = situation.conditions[m]; + let key = j + '-' + stageKey + initialDefinitionKey + '-' + 'takeProfit' + '-' + p + '-' + k + '-' + m; + + let value = false + if (conditions.get(key) !== undefined) { + value = conditions.get(key).value; + } + + if (value === false) { passed = false; } + } + + if (passed) { + + takeProfitPhase++; + takeProfitStage = 'Manage Stage' + if (stopLossPhase > 1) { strategyStage = 'Manage Stage' } + return; + } + } + } + } + + function calculateTakeProfit() { + + let strategy = tradingSystem.strategies[currentStrategyIndex]; + let openStage = strategy.openStage + let manageStage = strategy.manageStage + let phase + let key + + if (takeProfitStage === 'Open Stage' && openStage !== undefined) { + if (openStage.initialDefinition !== undefined) { + if (openStage.initialDefinition.takeProfit !== undefined) { + phase = openStage.initialDefinition.takeProfit.phases[takeProfitPhase - 1]; + key = currentStrategyIndex + '-' + 'openStage' + '-' + 'initialDefinition' + '-' + 'takeProfit' + '-' + (takeProfitPhase - 1); + } + } + } + + if (takeProfitStage === 'Manage Stage' && manageStage !== undefined) { + if (manageStage.takeProfit !== undefined) { + phase = manageStage.takeProfit.phases[takeProfitPhase - 2]; + key = currentStrategyIndex + '-' + 'manageStage' + '-' + 'takeProfit' + '-' + (takeProfitPhase - 2); + } + } + + if (phase !== undefined) { + if (phase.formula !== undefined) { + takeProfit = formulas.get(key) + } + } + } + + /* Checking if Stop or Take Profit were hit */ + if ( + (strategyStage === 'Open Stage' || strategyStage === 'Manage Stage') && + takePositionNow !== true + ) { + let strategy = tradingSystem.strategies[currentStrategyIndex]; + + /* Checking what happened since the last execution. We need to know if the Stop Loss + or our Take Profit were hit. */ + + /* Stop Loss condition: Here we verify if the Stop Loss was hitted or not. */ + + if ((baseAsset === 'BTC' && candle.max >= stopLoss) || (baseAsset !== 'BTC' && candle.min <= stopLoss)) { + + if (baseAsset === 'BTC') { + strategy.positionSize = balanceAssetB / stopLoss; + strategy.positionRate = stopLoss; + + balanceAssetA = balanceAssetA + balanceAssetB / stopLoss; + balanceAssetB = 0; + } else { + strategy.positionSize = balanceAssetA * stopLoss; + strategy.positionRate = stopLoss; + + balanceAssetB = balanceAssetB + balanceAssetA * stopLoss; + balanceAssetA = 0; + } + + if (currentDay !== undefined) { + if (positionInstant < currentDay.valueOf()) { + yesterday.balanceAssetA = balanceAssetA; + yesterday.balanceAssetB = balanceAssetB; + } + } + + marketRate = stopLoss; + type = '"Close@StopLoss"'; + strategyStage = 'Close Stage'; + stopLossStage = 'No Stage'; + takeProfitStage = 'No Stage'; + currentTrade.end = candle.end; + currentTrade.status = 1; + currentTrade.exitType = 1; + currentTrade.endRate = stopLoss; + + currentStrategy.number = currentStrategyIndex + currentStrategy.end = candle.end; + currentStrategy.endRate = candle.min; + currentStrategy.status = 1; + } + + /* Take Profit condition: Here we verify if the Take Profit was filled or not. */ + + if ((baseAsset === 'BTC' && candle.min <= takeProfit) || (baseAsset !== 'BTC' && candle.max >= takeProfit)) { + + if (baseAsset === 'BTC') { + strategy.positionSize = balanceAssetB / takeProfit; + strategy.positionRate = takeProfit; + + balanceAssetA = balanceAssetA + balanceAssetB / takeProfit; + balanceAssetB = 0; + } else { + strategy.positionSize = balanceAssetA * takeProfit; + strategy.positionRate = takeProfit; + + balanceAssetB = balanceAssetB + balanceAssetA * takeProfit; + balanceAssetA = 0; + } + + if (currentDay !== undefined) { + if (positionInstant < currentDay.valueOf()) { + yesterday.balanceAssetA = balanceAssetA; + yesterday.balanceAssetB = balanceAssetB; + + } + } + + marketRate = takeProfit; + type = '"Close@TakeProfit"'; + strategyStage = 'Close Stage'; + stopLossStage = 'No Stage'; + takeProfitStage = 'No Stage'; + currentTrade.end = candle.end; + currentTrade.status = 1; + currentTrade.exitType = 2; + currentTrade.endRate = takeProfit; + + currentStrategy.number = currentStrategyIndex + currentStrategy.end = candle.end; + currentStrategy.endRate = candle.min; + currentStrategy.status = 1; + } + } + + /* Taking a Position */ + if ( + takePositionNow === true + ) { + takePositionNow = false + + /* Position size and rate */ + let strategy = tradingSystem.strategies[currentStrategyIndex]; + + tradePositionSize = strategy.positionSize; + tradePositionRate = strategy.positionRate; + + /* We see if we need to put the actual order at the exchange. */ + + if (interExecutionMemory.executionContext !== undefined) { + switch (interExecutionMemory.executionContext.status) { + case "Without a Position": { // We need to put the order because It was not put yet. + if (strategy.openStage !== undefined) { + if (strategy.openStage.openExecution !== undefined) { + putOpeningOrder() + return + } + } + break + } + case "Position Closed": { // Waiting for a confirmation that the position was closed. + if (strategy.openStage !== undefined) { + if (strategy.openStage.openExecution !== undefined) { + putOpeningOrder() + return + } + } + break + } + case "Taking Position": { // Waiting for a confirmation that the position was taken. + break + } + case "In a Position": { // This should mean that we already put the order at the exchange. + break + } + } + } else { // The context does not exist so it means we are not in a position. + if (strategy.openStage !== undefined) { + if (strategy.openStage.openExecution !== undefined) { + putOpeningOrder() + return + } + } + } + + takePositionAtSimulation() + return + + function putOpeningOrder() { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> putOpeningOrder -> Entering function."); } + + /* We wont take a position unless we are withing the startDate and the endDate range */ + if (startDate !== undefined) { + if (candle.begin < startDate.valueOf()) { + takePositionAtSimulation() + return; + } + } + + if (endDate !== undefined) { + if (candle.begin > endDate.valueOf()) { + takePositionAtSimulation() + return; + } + } + + /* Checking the status of current positions */ + let positions = assistant.getPositions(); + if (positions.length > 0) { + let position = positions[positions.length - 1] // We are allways checking the the last position is not open. + if (position.status === 'open') { + afterLoop(); + return + } + } + + /* Mechanism to avoid putting the same order over and over again at different executions of the simulation engine. */ + + if (interExecutionMemory.executionContext !== undefined) { + if (interExecutionMemory.executionContext.periods !== undefined) { + if (periods <= interExecutionMemory.executionContext.periods) { + takePositionAtSimulation() + return; + } + } + } + + /* We are not going to place orders based on outdated information. The next filter prevents firing orders when backtesting. */ + let today = new Date(Math.trunc((new Date().valueOf()) / ONE_DAY_IN_MILISECONDS) * ONE_DAY_IN_MILISECONDS) + let processDay = new Date(Math.trunc(currentDay.valueOf() / ONE_DAY_IN_MILISECONDS) * ONE_DAY_IN_MILISECONDS) + if (today.valueOf() !== processDay.valueOf()) { + takePositionAtSimulation() + return; + } + + let openPositionRate + let amountA + let amountB + let positionDirection + + if (baseAsset === 'BTC') { + positionDirection = "sell" + + openPositionRate = tradePositionRate - 100 // Provisional stuff to simulate a market order. Put the price lower to get it executed quickly + + amountA = tradePositionSize * openPositionRate + amountB = tradePositionSize + } else { + positionDirection = "buy" + + openPositionRate = tradePositionRate + 100 // Provisional stuff to simulate a market order. Put the price higher to get it executed quickly + + amountA = tradePositionSize + amountB = tradePositionSize / openPositionRate + } + + interExecutionMemory.executionContext = { + status: "Taking Position" + } + + assistant.putPosition(positionDirection, openPositionRate, amountA, amountB, onOrderPut) + + function onOrderPut(err) { + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> putOpeningOrder -> onOrderPut -> Entering function."); } + + try { + switch (err.result) { + case global.DEFAULT_OK_RESPONSE.result: { + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> putOpeningOrder -> onOrderPut -> DEFAULT_OK_RESPONSE "); } + interExecutionMemory.executionContext = { + status: "In a Position", + periods: periods, + amountA: amountA, + amountB: amountB + } + takePositionAtSimulation() + return; + } + case global.DEFAULT_FAIL_RESPONSE.result: { + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> putOpeningOrder -> onOrderPut -> DEFAULT_FAIL_RESPONSE "); } + strategy.openStage.openExecution.error = err.message + afterLoop() + return; + } + case global.DEFAULT_RETRY_RESPONSE.result: { + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> putOpeningOrder -> onOrderPut -> DEFAULT_RETRY_RESPONSE "); } + strategy.openStage.openExecution.error = err.message + afterLoop() + return; + } + } + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[ERROR] runSimulation -> putOpeningOrder -> onOrderPut -> Unexpected Response -> Message = " + err.message); } + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] runSimulation -> putOpeningOrder -> onOrderPut -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return + } + } + } + + function takePositionAtSimulation() { + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> takePositionAtSimulation -> Entering function."); } + + /* Continue with the simulation */ + + calculateTakeProfit(); + calculateStopLoss(); + + marketRate = candle.close; + + previousBalanceAssetA = balanceAssetA; + previousBalanceAssetB = balanceAssetB; + + lastTradeProfitLoss = 0; + lastTradeROI = 0; + + if (baseAsset === 'BTC') { + balanceAssetB = balanceAssetB + tradePositionSize * tradePositionRate; + balanceAssetA = balanceAssetA - tradePositionSize; + } else { + balanceAssetA = balanceAssetA + tradePositionSize / tradePositionRate; + balanceAssetB = balanceAssetB - tradePositionSize; + } + + positionInstant = candle.end; + + if (currentDay !== undefined) { + if (positionInstant < currentDay.valueOf()) { + yesterday.balanceAssetA = balanceAssetA; + yesterday.balanceAssetB = balanceAssetB; + + yesterday.lastTradeProfitLoss = lastTradeProfitLoss; + yesterday.lastTradeROI = lastTradeROI; + } + } + + addRecord(); + controlLoop(); + return + } + } + + /* Closing a Position */ + if (strategyStage === 'Close Stage') { + + /* Position size and rate */ + let strategy = tradingSystem.strategies[currentStrategyIndex]; + + /* We see if we need to put the actual order at the exchange. */ + + if (interExecutionMemory.executionContext !== undefined) { + switch (interExecutionMemory.executionContext.status) { + case "Without a Position": { // No way to close anything at the exchange. + break + } + case "In a Position": { // This should mean that we already put the order at the exchange. + if (strategy.closeStage !== undefined) { + if (strategy.closeStage.closeExecution !== undefined) { + putClosingOrder() + return + } + } + break + } + case "Closing Position": { // Waiting for a confirmation that the position was taken. + if (strategy.closeStage !== undefined) { + if (strategy.closeStage.closeExecution !== undefined) { + putClosingOrder() + return + } + } + break + } + + case "Position Closed": { // + break + } + } + } + + closePositionAtSimulation() + return + + function putClosingOrder() { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> putClosingOrder -> Entering function."); } + + /* Checking the status of current positions */ + let positions = assistant.getPositions(); + if (positions.length > 0) { + let position = positions[positions.length - 1] // We are allways checking the the last position is not open. + if (position.status === 'open') { + afterLoop(); + return + } + } + + /* Mechanism to avoid putting the same order over and over again at different executions of the simulation engine. */ + + if (interExecutionMemory.executionContext !== undefined) { + if (interExecutionMemory.executionContext.periods !== undefined) { + if (periods <= interExecutionMemory.executionContext.periods) { + closePositionAtSimulation() + return; + } + } + } + + let closePositionRate + let amountA + let amountB + let positionDirection + + if (baseAsset === 'BTC') { + positionDirection = "buy" + + closePositionRate = ticker.last + 100; // This is provisional and totally arbitrary, until we have a formula on the designer that defines this stuff. + + amountA = interExecutionMemory.executionContext.amountA + amountB = interExecutionMemory.executionContext.amountA / closePositionRate + } else { + positionDirection = "sell" + + closePositionRate = ticker.last - 100; // This is provisional and totally arbitrary, until we have a formula on the designer that defines this stuff. + + amountA = interExecutionMemory.executionContext.amountB * closePositionRate + amountB = interExecutionMemory.executionContext.amountB + } + + interExecutionMemory.executionContext = { + status: "Closing Position" + } + + assistant.putPosition(positionDirection, closePositionRate, amountA, amountB, onOrderPut) + + function onOrderPut(err) { + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> putClosingOrder -> onOrderPut -> Entering function."); } + + try { + switch (err.result) { + case global.DEFAULT_OK_RESPONSE.result: { + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> putClosingOrder -> onOrderPut -> DEFAULT_OK_RESPONSE "); } + interExecutionMemory.executionContext = { + status: "Position Closed", + periods: periods, + amountA: amountA, + amountB: amountB + } + closePositionAtSimulation() + return; + } + case global.DEFAULT_FAIL_RESPONSE.result: { + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> putClosingOrder -> onOrderPut -> DEFAULT_FAIL_RESPONSE "); } + /* We will assume that the problem is temporary, and expect that it will work at the next execution.*/ + strategy.closeStage.closeExecution.error = err.message + afterLoop() + return; + } + case global.DEFAULT_RETRY_RESPONSE.result: { + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> putOpeningOrder -> onOrderPut -> DEFAULT_RETRY_RESPONSE "); } + strategy.closeStage.closeExecution.error = err.message + afterLoop() + return; + } + } + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[ERROR] runSimulation -> putClosingOrder -> onOrderPut -> Unexpected Response -> Message = " + err.message); } + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] runSimulation -> putClosingOrder -> onOrderPut -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return + } + } + } + + function closePositionAtSimulation() { + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> closePositionAtSimulation -> Entering function."); } + + roundtrips++; + + if (currentDay !== undefined) { + if (positionInstant < currentDay.valueOf()) { + yesterday.Roundtrips++; + } + } + + if (baseAsset === 'BTC') { + lastTradeProfitLoss = balanceAssetA - previousBalanceAssetA; + lastTradeROI = lastTradeProfitLoss * 100 / tradePositionSize; + if (isNaN(lastTradeROI)) { lastTradeROI = 0; } + profit = balanceAssetA - initialBalanceA; + } else { + lastTradeProfitLoss = balanceAssetB - previousBalanceAssetB; + lastTradeROI = lastTradeProfitLoss * 100 / tradePositionSize; + if (isNaN(lastTradeROI)) { lastTradeROI = 0; } + profit = balanceAssetB - initialBalanceB; + } + + if (currentDay !== undefined) { + if (positionInstant < currentDay.valueOf()) { + yesterday.lastTradeProfitLoss = lastTradeProfitLoss; + yesterday.profit = profit; + yesterday.lastTradeROI = lastTradeROI; + } + } + + currentTrade.lastTradeROI = lastTradeROI; + currentTrade.stopRate = stopLoss; + + if (lastTradeProfitLoss > 0) { + hits++; + + if (currentDay !== undefined) { + if (positionInstant < currentDay.valueOf()) { + yesterday.hits++; + } + } + + } else { + fails++; + + if (currentDay !== undefined) { + if (positionInstant < currentDay.valueOf()) { + yesterday.fails++; + } + } + } + + if (baseAsset === 'BTC') { + ROI = (initialBalanceA + profit) / initialBalanceA - 1; + hitRatio = hits / roundtrips; + anualizedRateOfReturn = ROI / days * 365; + } else { + ROI = (initialBalanceB + profit) / initialBalanceB - 1; + hitRatio = hits / roundtrips; + anualizedRateOfReturn = ROI / days * 365; + } + + if (currentDay !== undefined) { + if (positionInstant < currentDay.valueOf()) { + yesterday.ROI = ROI; + yesterday.hitRatio = hitRatio; + yesterday.anualizedRateOfReturn = anualizedRateOfReturn; + } + } + + addRecord(); + + currentStrategyIndex = -1; + stopLoss = 0; + tradePositionRate = 0; + tradePositionSize = 0; + positionInstant = undefined; + takeProfit = 0; + strategyStage = 'No Stage'; + stopLossStage = 'No Stage'; + takeProfitStage = 'No Stage'; + stopLossPhase = 0; + takeProfitPhase = 0; + controlLoop(); + return + } + } + + /* Not a buy or sell condition */ + + marketRate = candle.close; + addRecord(); + controlLoop(); + return + + function addRecord() { + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> addRecord -> Entering function."); } + + // Since we are going to write the message to a file that the Simulation Executor is going to read, we use the abbreviations. + let messageType; + let message; + let simulationRecord; + let executionRecord; + let executionMessage; + + messageId++; + + if (strategyStage === 'Open Stage' || strategyStage === 'Manage Stage' || type === '"Close@TakeProfit"' || type === '"Close@StopLoss"') { + + if (type === '"Take Position"') { + messageType = MESSAGE_TYPE.Order; + orderId++; + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> addRecord -> Taking Position Now. "); } + + } else { + if (type === '"Close@TakeProfit"' || type === '"Close@StopLoss"') { + messageType = MESSAGE_TYPE.OrderClose; + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> addRecord -> Closing Position Now. "); } + + } else { + messageType = MESSAGE_TYPE.OrderUpdate; + } + } + + let exitOutcome = "" + if (messageType === MESSAGE_TYPE.OrderClose && type === '"Close@TakeProfit"') { + exitOutcome = "TP" + } + if (messageType === MESSAGE_TYPE.OrderClose && type === '"Close@StopLoss"') { + exitOutcome = "SL" + } + + executionMessage = createMessage( + messageId, + MESSAGE_ENTITY.SimulationEngine, + MESSAGE_ENTITY.SimulationExecutor, + messageType, + (new Date()).valueOf(), + orderId.toString(), + ORDER_CREATOR.SimulationEngine, + (new Date()).valueOf(), + ORDER_OWNER.User, + global.EXCHANGE_NAME, + "BTC_USDT", + 0, + ORDER_TYPE.Limit, + tradePositionRate, + stopLoss, + takeProfit, + ORDER_DIRECTION.Sell, + -1, + ORDER_STATUS.Signaled, + 0, + exitOutcome, + "") + + } + else { + + executionMessage = createMessage( + messageId, + MESSAGE_ENTITY.SimulationEngine, + MESSAGE_ENTITY.SimulationExecutor, + MESSAGE_TYPE.HeartBeat, + (new Date()).valueOf(), + "", + "", + 0, + "", + "", + "", + 0, + "", + 0, + 0, + 0, + "", + 0, + "", + 0, + "", + "") + } + + executionRecord = { + begin: candle.begin, + end: candle.end, + executionRecord: executionMessage + } + + executionArray.push(executionRecord) + + let strategyStageNumber + switch (strategyStage) { + case 'No Stage': { + strategyStageNumber = 0 + break + } + case 'Trigger Stage': { + strategyStageNumber = 1 + break + } + case 'Open Stage': { + strategyStageNumber = 2 + break + } + case 'Manage Stage': { + strategyStageNumber = 3 + break + } + case 'Close Stage': { + strategyStageNumber = 4 + break + } + } + + if (balanceAssetA === Infinity) { + balanceAssetA = Number.MAX_SAFE_INTEGER + } + + if (balanceAssetB === Infinity) { + balanceAssetB = Number.MAX_SAFE_INTEGER + } + + let quotedBaseAsset = '"' + baseAsset + '"' + + simulationRecord = { + begin: candle.begin, + end: candle.end, + type: type, + marketRate: marketRate, + amount: 1, + balanceA: balanceAssetA, + balanceB: balanceAssetB, + profit: profit, + lastTradeProfitLoss: lastTradeProfitLoss, + stopLoss: stopLoss, + roundtrips: roundtrips, + hits: hits, + fails: fails, + hitRatio: hitRatio, + ROI: ROI, + periods: periods, + days: days, + anualizedRateOfReturn: anualizedRateOfReturn, + positionRate: tradePositionRate, + lastTradeROI: lastTradeROI, + strategy: currentStrategyIndex, + strategyStageNumber: strategyStageNumber, + takeProfit: takeProfit, + stopLossPhase: stopLossPhase, + takeProfitPhase: takeProfitPhase, + executionRecord: executionMessage, + positionSize: tradePositionSize, + initialBalanceA: initialBalanceA, + minimumBalanceA: minimumBalanceA, + maximumBalanceA: maximumBalanceA, + initialBalanceB: initialBalanceB, + minimumBalanceB: minimumBalanceB, + maximumBalanceB: maximumBalanceB, + baseAsset: quotedBaseAsset + } + + recordsArray.push(simulationRecord); + + type = '""'; + + /* Prepare the information for the Conditions File */ + + conditionsArrayRecord.push(currentStrategyIndex); + conditionsArrayRecord.push(strategyStageNumber); + conditionsArrayRecord.push(stopLossPhase); + conditionsArrayRecord.push(takeProfitPhase); + conditionsArrayRecord.push(conditionsArrayValues); + conditionsArrayRecord.push(formulasErrors); + conditionsArrayRecord.push(formulasValues); + + conditionsArray.push(conditionsArrayRecord); + + /* Prepare the information for the Strategies File*/ + + if ( + (currentStrategy.begin !== 0 && currentStrategy.end !== 0) || + (currentStrategy.begin !== 0 && i === candles.length - 1 && lastCandle.end !== lastInstantOfTheDay) + ) { + + 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 && lastCandle.end !== lastInstantOfTheDay) + ) { + + currentTrade.profit = lastTradeProfitLoss; + + tradesArray.push(currentTrade); + + currentTrade = { + begin: 0, + end: 0, + status: 0, + lastTradeROI: 0, + exitType: 0, + beginRate: 0, + endRate: 0 + } + } + } + } + + function controlLoop() { + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> controlLoop -> Entering function."); } + i++ + if (i < candles.length) { + loop() + } else { + afterLoop() + } + } + + function afterLoop() { + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> afterLoop -> Entering function."); } + + /* + Before returning we need to see if we have to record some of our counters at the interExecutionMemory. + To do that, the condition to be met is that this execution must include all candles of the currentDay. + */ + + if (currentDay !== undefined) { + + if (lastCandle.end === lastInstantOfTheDay) { + + interExecutionMemory.balanceAssetA = yesterday.balanceAssetA; + interExecutionMemory.balanceAssetB = yesterday.balanceAssetB; + interExecutionMemory.lastTradeProfitLoss = yesterday.lastTradeProfitLoss; + interExecutionMemory.profit = yesterday.profit; + interExecutionMemory.lastTradeROI = yesterday.lastTradeROI; + + interExecutionMemory.roundtrips = interExecutionMemory.roundtrips + yesterday.Roundtrips; + interExecutionMemory.fails = interExecutionMemory.fails + yesterday.fails; + interExecutionMemory.hits = interExecutionMemory.hits + yesterday.hits; + interExecutionMemory.periods = interExecutionMemory.periods + yesterday.Periods; + + interExecutionMemory.messageId = interExecutionMemory.messageId + yesterday.messageId; + interExecutionMemory.orderId = interExecutionMemory.orderId + yesterday.orderId; + + interExecutionMemory.hitRatio = yesterday.hitRatio; + interExecutionMemory.ROI = yesterday.ROI; + interExecutionMemory.anualizedRateOfReturn = yesterday.anualizedRateOfReturn; + } + } + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> callback -> recordsArray.length = " + recordsArray.length); } + + callback(tradingSystem, executionArray, recordsArray, conditionsArray, strategiesArray, tradesArray, lastObjectsArray); + } + + + function getElement(pArray, begin, end) { + + let element; + + for (let i = 0; i < pArray.length; i++) { + + element = pArray[i]; + + if (begin >= element.begin && end <= element.end) { + return element + } + } + + return + } + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] runSimulation -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function buildLRC(dataFile, callBackFunction) { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] buildLRC -> Entering function."); } + + try { + + let previous; + + for (let i = 0; i < dataFile.length; i++) { + + let LRC = { + begin: dataFile[i][0], + end: dataFile[i][1], + _15: dataFile[i][2], + _30: dataFile[i][3], + _60: dataFile[i][4] + }; + + if (previous !== undefined) { + + if (previous._15 > LRC._15) { LRC.direction15 = 'Down'; } + if (previous._15 < LRC._15) { LRC.direction15 = 'Up'; } + if (previous._15 === LRC._15) { LRC.direction15 = 'Side'; } + + if (previous._30 > LRC._30) { LRC.direction30 = 'Down'; } + if (previous._30 < LRC._30) { LRC.direction30 = 'Up'; } + if (previous._30 === LRC._30) { LRC.direction30 = 'Side'; } + + if (previous._60 > LRC._60) { LRC.direction60 = 'Down'; } + if (previous._60 < LRC._60) { LRC.direction60 = 'Up'; } + if (previous._60 === LRC._60) { LRC.direction60 = 'Side'; } + + } + + LRC.previous = previous; + + LRCMap.set(LRC.begin, LRC); + + previous = LRC; + } + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] buildLRC -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function buildPercentageBandwidthMap(dataFile, callBackFunction) { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] buildPercentageBandwidthMap -> Entering function."); } + + try { + + let previous; + + for (let i = 0; i < dataFile.length; i++) { + + let percentageBandwidth = { + begin: dataFile[i][0], + end: dataFile[i][1], + value: dataFile[i][2], + movingAverage: dataFile[i][3], + bandwidth: dataFile[i][4] + }; + + if (previous !== undefined) { + + if (previous.movingAverage > percentageBandwidth.movingAverage) { percentageBandwidth.direction = 'Down'; } + if (previous.movingAverage < percentageBandwidth.movingAverage) { percentageBandwidth.direction = 'Up'; } + if (previous.movingAverage === percentageBandwidth.movingAverage) { percentageBandwidth.direction = 'Side'; } + + } + + percentageBandwidth.previous = previous; + + percentageBandwidthMap.set(percentageBandwidth.begin, percentageBandwidth); + + previous = percentageBandwidth; + } + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] buildPercentageBandwidthMap -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function buildBollingerBandsMap(dataFile, callBackFunction) { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] buildBollingerBandsMap -> Entering function."); } + + try { + + let previous; + + for (let i = 0; i < dataFile.length; i++) { + + let bollingerBand = { + begin: dataFile[i][0], + end: dataFile[i][1], + movingAverage: dataFile[i][2], + standardDeviation: dataFile[i][3], + deviation: dataFile[i][4] + }; + + if (previous !== undefined) { + + if (previous.movingAverage > bollingerBand.movingAverage) { bollingerBand.direction = 'Down'; } + if (previous.movingAverage < bollingerBand.movingAverage) { bollingerBand.direction = 'Up'; } + if (previous.movingAverage === bollingerBand.movingAverage) { bollingerBand.direction = 'Side'; } + + } + + bollingerBand.previous = previous; + + bollingerBandsMap.set(bollingerBand.begin, bollingerBand); + + previous = bollingerBand; + } + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] buildBollingerBandsMap -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function buildBollingerChannelsArray(dataFile, callBackFunction) { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] buildBollingerChannelsArray -> Entering function."); } + + try { + + let previous; + + for (let i = 0; i < dataFile.length; i++) { + + let bollingerChannel = { + begin: dataFile[i][0], + end: dataFile[i][1], + direction: dataFile[i][2], + period: dataFile[i][3] + }; + + bollingerChannel.previous = previous; + + bollingerChannelsArray.push(bollingerChannel); + + previous = bollingerChannel; + } + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] buildBollingerChannelsArray -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function buildBollingerSubChannelsArray(dataFile, callBackFunction) { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] buildBollingerSubChannelsArray -> Entering function."); } + + try { + + let previous; + + for (let i = 0; i < dataFile.length; i++) { + + let bollingerSubChannel = { + begin: dataFile[i][0], + end: dataFile[i][1], + direction: dataFile[i][2], + slope: dataFile[i][3], + period: dataFile[i][4], + firstMovingAverage: dataFile[i][5], + lastMovingAverage: dataFile[i][6], + firstDeviation: dataFile[i][7], + lastDeviation: dataFile[i][8] + }; + + bollingerSubChannel.previous = previous; + + bollingerSubChannelsArray.push(bollingerSubChannel); + + previous = bollingerSubChannel; + } + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] buildBollingerSubChannelsArray -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function buildCandles(dataFile, callBackFunction) { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] buildCandles -> Entering function."); } + + try { + + let previous; + + for (let i = 0; i < dataFile.length; i++) { + + let candle = { + open: undefined, + close: undefined, + min: 10000000000000, + max: 0, + begin: undefined, + end: undefined, + direction: undefined + }; + + candle.min = dataFile[i][0]; + candle.max = dataFile[i][1]; + + candle.open = dataFile[i][2]; + candle.close = dataFile[i][3]; + + candle.begin = dataFile[i][4]; + candle.end = dataFile[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'; } + + candle.previous = previous; + + candles.push(candle); + + previous = candle; + } + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] buildCandles -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } +}; + + + diff --git a/Context.js b/Context.js index 16949f4d8..7d55ef52e 100644 --- a/Context.js +++ b/Context.js @@ -161,8 +161,8 @@ if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] initialize -> getExecutionHistory -> Entering function."); } - let fileName = "/Execution.History." + bot.startMode + "." + runIndex + ".json"; - let filePath = bot.filePathRoot + "/Output/" + bot.process + fileName; + let fileName = "Execution.History." + bot.startMode + "." + runIndex + ".json"; + let filePath = bot.filePathRoot + "/Output/Trading-Process/" + fileName; fileStorage.getTextFile(global.DEV_TEAM, filePath, onFileReceived); @@ -223,7 +223,7 @@ let fileName = "/Execution.Context." + bot.startMode + "." + runIndex + ".json"; let dateForPath = date.getUTCFullYear() + '/' + utilities.pad(date.getUTCMonth() + 1, 2) + '/' + utilities.pad(date.getUTCDate(), 2) + '/' + utilities.pad(date.getUTCHours(), 2) + '/' + utilities.pad(date.getUTCMinutes(), 2); - let filePath = bot.filePathRoot + "/Output/" + bot.process + '/' + dateForPath + fileName; + let filePath = bot.filePathRoot + "/Output/Trading-Process/" + dateForPath + fileName; fileStorage.getTextFile(global.DEV_TEAM, filePath, onFileReceived); @@ -397,7 +397,7 @@ let fileName = "/Execution.Context." + bot.startMode + "." + runIndex +".json"; let dateForPath = bot.processDatetime.getUTCFullYear() + '/' + utilities.pad(bot.processDatetime.getUTCMonth() + 1, 2) + '/' + utilities.pad(bot.processDatetime.getUTCDate(), 2) + '/' + utilities.pad(bot.processDatetime.getUTCHours(), 2) + '/' + utilities.pad(bot.processDatetime.getUTCMinutes(), 2); - let filePath = bot.filePathRoot + "/Output/" + bot.process + '/' + dateForPath + fileName; + let filePath = bot.filePathRoot + "/Output/Trading-Process/" + dateForPath + fileName; let fileContent = JSON.stringify(thisObject.executionContext); if(fileContent === undefined){ @@ -436,8 +436,8 @@ if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] saveThemAll -> writeExucutionHistory -> Entering function."); } - let fileName = "/Execution.History." + bot.startMode + "." + runIndex + ".json"; - let filePath = bot.filePathRoot + "/Output/" + bot.process + fileName; + let fileName = "Execution.History." + bot.startMode + "." + runIndex + ".json"; + let filePath = bot.filePathRoot + "/Output/Trading-Process/" + fileName; let newRecord = [ thisObject.newHistoryRecord.date.valueOf(), @@ -487,8 +487,8 @@ /* Here we will write the file containing the max sequence number. */ fileContent = runIndex; - fileName = "/Execution.History." + bot.startMode + "." + "Sequence" + ".json"; - filePath = bot.filePathRoot + "/Output/" + bot.process + fileName; + fileName = "Execution.History." + bot.startMode + "." + "Sequence" + ".json"; + filePath = bot.filePathRoot + "/Output/Trading-Process/" + fileName; fileStorage.createTextFile(global.DEV_TEAM, filePath, fileContent + '\n', onSequenceFileCreated); diff --git a/Project.njsproj b/Project.njsproj index a23e91de9..44675ce23 100644 --- a/Project.njsproj +++ b/Project.njsproj @@ -24,6 +24,7 @@ Code + @@ -36,6 +37,7 @@ Code + Code diff --git a/Run.js b/Run.js index bbbb3cb21..41af39c4c 100644 --- a/Run.js +++ b/Run.js @@ -1,4 +1,5 @@ require('dotenv').config() +const strategy = require('./Integrations/Strategy') global.SHALL_BOT_STOP = false; global.AT_BREAKPOINT = false; // This is used only when running at the browser. @@ -29,9 +30,31 @@ process.on('exit', function (code) { readExecutionConfiguration(); -function readExecutionConfiguration() { +async function readExecutionConfiguration() { try { console.log("[INFO] Run -> readExecutionConfiguration -> Entering function. "); + + /*Try to get the begin and end dates from the Definition*/ + let definition = await strategy.getStrategy(); + let initialDatetime = process.env.BEGIN_DATE_TIME + let finalDatetime = process.env.END_DATE_TIME + + if (definition !== undefined) { + if (definition.tradingSystem !== undefined) { + if (definition.tradingSystem.parameters !== undefined) { + if (definition.tradingSystem.parameters.baseAsset !== undefined) { + if (definition.tradingSystem.parameters.baseAsset.formula !== undefined) { + if (definition.tradingSystem.parameters.baseAsset.formula.code !== undefined) { + let code = JSON.parse(definition.tradingSystem.parameters.baseAsset.formula.code) + initialDatetime = code.initialDatetime + finalDatetime = code.finalDatetime + } + } + } + } + } + } + let startMode // General Financial Being Configuration @@ -42,22 +65,22 @@ function readExecutionConfiguration() { let live = { run: "false", resumeExecution: process.env.RESUME_EXECUTION, - beginDatetime: process.env.BEGIN_DATE_TIME, - endDatetime: process.env.END_DATE_TIME + beginDatetime: initialDatetime, + endDatetime: finalDatetime } let backtest = { run: "false", resumeExecution: process.env.RESUME_EXECUTION, - beginDatetime: process.env.BEGIN_DATE_TIME, - endDatetime: process.env.END_DATE_TIME + beginDatetime: initialDatetime, + endDatetime: finalDatetime } let competition = { run: "false", resumeExecution: process.env.RESUME_EXECUTION, - beginDatetime: process.env.BEGIN_DATE_TIME, - endDatetime: process.env.END_DATE_TIME + beginDatetime: initialDatetime, + endDatetime: finalDatetime } startMode = { diff --git a/TradingEngineProcessMainLoop.js b/TradingEngineProcessMainLoop.js index a156cf1c1..6c70bf3af 100644 --- a/TradingEngineProcessMainLoop.js +++ b/TradingEngineProcessMainLoop.js @@ -54,7 +54,8 @@ } USER_BOT_MODULE = {} - USER_BOT_MODULE.newUserBot = eval(text); // TODO This needs to be changed function + USER_BOT_MODULE.newUserBot = require(ROOT_DIR + 'User.Bot').newUserBot // Use this for a better debugging experience. You need to bring this js module to this folder in order to work. + //USER_BOT_MODULE.newUserBot = eval(text); // Use this for production filePath = global.DEV_TEAM + "/" + "bots" + "/" + bot.repo; filePath += "/Commons.js" @@ -69,7 +70,8 @@ return; } COMMONS_MODULE = {} - COMMONS_MODULE.newCommons = eval(text); // TODO This needs to be changed function + COMMONS_MODULE.newCommons = require(ROOT_DIR + 'Commons').newCommons // Use this for a better debugging experience. You need to bring this js module to this folder in order to work. + //COMMONS_MODULE.newCommons = eval(text); // Use this for production callBackFunction(global.DEFAULT_OK_RESPONSE); } diff --git a/User.Bot.js b/User.Bot.js new file mode 100644 index 000000000..1e457f5a7 --- /dev/null +++ b/User.Bot.js @@ -0,0 +1,556 @@ +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 utilities = UTILITIES.newCloudUtilities(bot, logger); + + let dataDependencies; + let assistant; + + return thisObject; + + function initialize(pDataDependencies, callBackFunction, pAssistant) { + + try { + + logger.fileName = MODULE_NAME; + logger.initialize(); + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] initialize -> Entering function."); } + + dataDependencies = pDataDependencies; + assistant = pAssistant; + + 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, currentDay, startDate, endDate, interExecutionMemory, 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 = {}; + + 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, + assistant, + 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]; + + /* 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 + "," + + JSON.stringify(record.executionRecord) + "]"; + + 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.CLONE_EXECUTOR.codeName + "." + global.CLONE_EXECUTOR.version + "/" + global.EXCHANGE_NAME + "/" + bot.dataSetVersion; + let filePath = filePathRoot + "/Output/" + EXECUTION_FOLDER_NAME + "/" + "Multi-Period-Daily" + "/" + outputPeriodLabel + "/" + dateForPath; + 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]; + + /* 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.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 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.CLONE_EXECUTOR.codeName + "." + global.CLONE_EXECUTOR.version + "/" + global.EXCHANGE_NAME + "/" + bot.dataSetVersion; + let filePath = filePathRoot + "/Output/" + SIMULATED_RECORDS_FOLDER_NAME + "/" + "Multi-Period-Daily" + "/" + outputPeriodLabel + "/" + dateForPath; + 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]; + + /* Will only add to the file the records of the current day */ + + if (record.begin < currentDay.valueOf()) { continue; } + + let conditions = ""; + let conditionsSeparator = ""; + + /* Will only add to the file the records of the current day */ + + if (record.begin < currentDay.valueOf()) { continue; } + + 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 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.CLONE_EXECUTOR.codeName + "." + global.CLONE_EXECUTOR.version + "/" + global.EXCHANGE_NAME + "/" + bot.dataSetVersion; + let filePath = filePathRoot + "/Output/" + CONDITIONS_FOLDER_NAME + "/" + "Multi-Period-Daily" + "/" + outputPeriodLabel + "/" + dateForPath; + 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]; + + /* Will only add to the file the records of the current day. In this case since objects can span more than one day, we add all of the objects that ends + at the current date. */ + + if (record.end < 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.CLONE_EXECUTOR.codeName + "." + global.CLONE_EXECUTOR.version + "/" + global.EXCHANGE_NAME + "/" + bot.dataSetVersion; + let filePath = filePathRoot + "/Output/" + STRATEGIES_FOLDER_NAME + "/" + "Multi-Period-Daily" + "/" + outputPeriodLabel + "/" + dateForPath; + 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]; + + /* Will only add to the file the records of the current day. In this case since objects can span more than one day, we add all of the objects that ends + at the current date. */ + + if (record.end < currentDay.valueOf()) { continue; } + 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 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.CLONE_EXECUTOR.codeName + "." + global.CLONE_EXECUTOR.version + "/" + global.EXCHANGE_NAME + "/" + bot.dataSetVersion; + let filePath = filePathRoot + "/Output/" + TRADES_FOLDER_NAME + "/" + "Multi-Period-Daily" + "/" + outputPeriodLabel + "/" + dateForPath; + 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); + } + } +}; From 0826bf8e67c9ec942f70ca2580e398d895d0274b Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Wed, 7 Aug 2019 23:22:42 +0200 Subject: [PATCH 11/19] Better Logging --- Context.js | 1 + 1 file changed, 1 insertion(+) diff --git a/Context.js b/Context.js index 7d55ef52e..62adb5a56 100644 --- a/Context.js +++ b/Context.js @@ -114,6 +114,7 @@ } default: { logger.write(MODULE_NAME, "[ERROR] initialize -> onDone -> Operation Failed with custom response."); + logger.write(MODULE_NAME, "[ERROR] initialize -> onDone -> err = " + JSON.stringify(err)); callBackFunction(err); } } From 1d2bcdb1180e67d9b855fa6d109b1b114743c5d9 Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Thu, 8 Aug 2019 14:40:20 +0200 Subject: [PATCH 12/19] sequence added --- Project.njsproj | 1 + sequence - all processes.json | 158 ++++++++++++++++++++++++++++++++++ sequence.json | 7 +- 3 files changed, 162 insertions(+), 4 deletions(-) create mode 100644 sequence - all processes.json diff --git a/Project.njsproj b/Project.njsproj index 44675ce23..1d52c4aa0 100644 --- a/Project.njsproj +++ b/Project.njsproj @@ -75,6 +75,7 @@ + diff --git a/sequence - all processes.json b/sequence - all processes.json new file mode 100644 index 000000000..29b3927d5 --- /dev/null +++ b/sequence - all processes.json @@ -0,0 +1,158 @@ +[ + { + "devTeam": "AAMasters", + "bot": "AACharly", + "mode": "noTime", + "resumeExecution": true, + "type": "Sensor", + "exchangeName": "Poloniex", + "process": "Live-Trades", + "startYear": 2019, + "endYear": 2021, + "month": 1 + }, + { + "devTeam": "AAMasters", + "bot": "AACharly", + "mode": "noTime", + "resumeExecution": true, + "type": "Sensor", + "exchangeName": "Poloniex", + "process": "Hole-Fixing", + "startYear": 2019, + "endYear": 2021, + "month": 1 + }, + { + "devTeam": "AAMasters", + "bot": "AABruce", + "mode": "allMonths", + "resumeExecution": true, + "type": "Indicator", + "exchangeName": "Poloniex", + "process": "Single-Period-Daily", + "startYear": 2019, + "endYear": 2021, + "month": 1 + }, + { + "devTeam": "AAMasters", + "bot": "AAOlivia", + "mode": "noTime", + "resumeExecution": true, + "type": "Indicator", + "exchangeName": "Poloniex", + "process": "Multi-Period-Daily", + "startYear": 2019, + "endYear": 2021, + "month": 1 + }, + { + "devTeam": "AAMasters", + "bot": "AAOlivia", + "mode": "noTime", + "resumeExecution": true, + "type": "Indicator", + "exchangeName": "Poloniex", + "process": "Multi-Period-Market", + "startYear": 2019, + "endYear": 2021, + "month": 1 + }, + { + "devTeam": "AAMasters", + "bot": "AATom", + "mode": "noTime", + "resumeExecution": true, + "type": "Indicator", + "exchangeName": "Poloniex", + "process": "Multi-Period-Daily", + "startYear": 2019, + "endYear": 2021, + "month": 1 + }, + { + "devTeam": "AAMasters", + "bot": "AATom", + "mode": "noTime", + "resumeExecution": true, + "type": "Indicator", + "exchangeName": "Poloniex", + "process": "Multi-Period-Market", + "startYear": 2019, + "endYear": 2021, + "month": 1 + }, + { + "devTeam": "AAMasters", + "bot": "AAChris", + "mode": "noTime", + "resumeExecution": true, + "type": "Indicator", + "exchangeName": "Poloniex", + "process": "Multi-Period-Daily", + "startYear": 2019, + "endYear": 2021, + "month": 1 + }, + { + "devTeam": "AAMasters", + "bot": "AAChris", + "mode": "noTime", + "resumeExecution": true, + "type": "Indicator", + "exchangeName": "Poloniex", + "process": "Multi-Period-Market", + "startYear": 2019, + "endYear": 2021, + "month": 1 + }, + { + "devTeam": "AAMasters", + "bot": "AAPaula", + "mode": "noTime", + "resumeExecution": true, + "type": "Indicator", + "exchangeName": "Poloniex", + "process": "Multi-Period-Daily", + "startYear": 2019, + "endYear": 2021, + "month": 1 + }, + { + "devTeam": "AAMasters", + "bot": "AAPaula", + "mode": "noTime", + "resumeExecution": true, + "type": "Indicator", + "exchangeName": "Poloniex", + "process": "Multi-Period-Market", + "startYear": 2019, + "endYear": 2021, + "month": 1 + }, + { + "devTeam": "AAVikings", + "bot": "AAGauss", + "mode": "noTime", + "resumeExecution": true, + "type": "Indicator", + "exchangeName": "Poloniex", + "process": "Multi-Period-Daily", + "startYear": 2019, + "endYear": 2021, + "month": 1 + }, + { + "devTeam": "AAVikings", + "bot": "AAGauss", + "mode": "noTime", + "resumeExecution": true, + "type": "Indicator", + "exchangeName": "Poloniex", + "process": "Multi-Period-Market", + "startYear": 2019, + "endYear": 2021, + "month": 1 + } +] diff --git a/sequence.json b/sequence.json index ae710da07..a0612a964 100644 --- a/sequence.json +++ b/sequence.json @@ -1,13 +1,12 @@ [ - { "devTeam": "AAMasters", - "bot": "AAPaula", + "bot": "AACharly", "mode": "noTime", "resumeExecution": true, - "type": "Indicator", + "type": "Sensor", "exchangeName": "Poloniex", - "process": "Multi-Period-Daily", + "process": "Live-Trades", "startYear": 2019, "endYear": 2021, "month": 1 From a5f60d529580378b430811cac473c9d300bf0df9 Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Thu, 8 Aug 2019 14:53:54 +0200 Subject: [PATCH 13/19] New Sequence --- sequence - all processes.json | 66 ++++++++++++++++++++++++----------- 1 file changed, 45 insertions(+), 21 deletions(-) diff --git a/sequence - all processes.json b/sequence - all processes.json index 29b3927d5..740841b6a 100644 --- a/sequence - all processes.json +++ b/sequence - all processes.json @@ -11,22 +11,23 @@ "endYear": 2021, "month": 1 }, - { - "devTeam": "AAMasters", - "bot": "AACharly", - "mode": "noTime", - "resumeExecution": true, - "type": "Sensor", - "exchangeName": "Poloniex", - "process": "Hole-Fixing", - "startYear": 2019, - "endYear": 2021, - "month": 1 - }, + { + "devTeam": "AAMasters", + "bot": "AACharly", + "mode": "noTime", + "resumeExecution": true, + "type": "Sensor", + "exchangeName": "Poloniex", + "process": "Hole-Fixing", + "startYear": 2019, + "endYear": 2021, + "month": 1 + }, + { "devTeam": "AAMasters", "bot": "AABruce", - "mode": "allMonths", + "mode": "noTime", "resumeExecution": true, "type": "Indicator", "exchangeName": "Poloniex", @@ -112,9 +113,9 @@ "bot": "AAPaula", "mode": "noTime", "resumeExecution": true, - "type": "Indicator", + "type": "Sensor", "exchangeName": "Poloniex", - "process": "Multi-Period-Daily", + "process": "Live-Trades", "startYear": 2019, "endYear": 2021, "month": 1 @@ -143,16 +144,39 @@ "endYear": 2021, "month": 1 }, + { + "devTeam": "AAVikings", + "bot": "AAGauss", + "mode": "noTime", + "resumeExecution": true, + "type": "Indicator", + "exchangeName": "Poloniex", + "process": "Multi-Period-Market", + "startYear": 2019, + "endYear": 2021, + "month": 1 + }, { - "devTeam": "AAVikings", - "bot": "AAGauss", + "devTeam": "vikingsalerts", + "bot": "simulator-bot-vikingsalerts", "mode": "noTime", "resumeExecution": true, "type": "Indicator", "exchangeName": "Poloniex", - "process": "Multi-Period-Market", - "startYear": 2019, - "endYear": 2021, - "month": 1 + "process": "Multi-Period-Daily", + "beginDatetime": "2019-08-01T08:00:00.000Z", + "timePeriod": "05-min" + }, + { + "devTeam": "vikingsalerts", + "bot": "bot-vikingsalerts", + "mode": "live", + "resumeExecution": true, + "type": "Trading", + "exchangeName": "Poloniex", + "process": "Trading-Process", + "beginDatetime": "2019-08-01T08:00:00.000Z", + "timePeriod": "05-min", + "dataSet": "Multi-Period-Daily" } ] From e34bb56cfc002bfb80148a966988ab9b60ba0eda Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Thu, 8 Aug 2019 15:00:42 +0200 Subject: [PATCH 14/19] Manual Merging --- Project.njsproj | 1 + Run.js | 196 +++++++++++++++++++++++++----------------------- 2 files changed, 102 insertions(+), 95 deletions(-) diff --git a/Project.njsproj b/Project.njsproj index 1d52c4aa0..b5d1b8cbb 100644 --- a/Project.njsproj +++ b/Project.njsproj @@ -75,6 +75,7 @@ + diff --git a/Run.js b/Run.js index 56e766514..fc714493e 100644 --- a/Run.js +++ b/Run.js @@ -97,104 +97,121 @@ function onExecutionFinish (result, finishStepKey) { } } -async function readExecutionConfiguration () { - try { - console.log('[INFO] Run -> readExecutionConfiguration -> Entering function. ') + +async function readExecutionConfiguration() { + try { + console.log("[INFO] Run -> readExecutionConfiguration -> Entering function. "); /* Try to get the begin and end dates from the Definition */ - let definition = await strategy.getStrategy() - let initialDatetime = process.env.BEGIN_DATE_TIME - let finalDatetime = process.env.END_DATE_TIME + let definition = await strategy.getStrategy() + let initialDatetime = process.env.BEGIN_DATE_TIME + let finalDatetime = process.env.END_DATE_TIME - if (definition !== undefined) { - if (definition.tradingSystem !== undefined) { - if (definition.tradingSystem.parameters !== undefined) { - if (definition.tradingSystem.parameters.baseAsset !== undefined) { - if (definition.tradingSystem.parameters.baseAsset.formula !== undefined) { - if (definition.tradingSystem.parameters.baseAsset.formula.code !== undefined) { - let code = JSON.parse(definition.tradingSystem.parameters.baseAsset.formula.code) - initialDatetime = code.initialDatetime - finalDatetime = code.finalDatetime - } + if (definition !== undefined) { + if (definition.tradingSystem !== undefined) { + if (definition.tradingSystem.parameters !== undefined) { + if (definition.tradingSystem.parameters.baseAsset !== undefined) { + if (definition.tradingSystem.parameters.baseAsset.formula !== undefined) { + if (definition.tradingSystem.parameters.baseAsset.formula.code !== undefined) { + let code = JSON.parse(definition.tradingSystem.parameters.baseAsset.formula.code) + initialDatetime = code.initialDatetime + finalDatetime = code.finalDatetime + } + } + } + } } - } } - } - } - let startMode + let startMode // General Financial Being Configuration - global.DEV_TEAM = process.env.DEV_TEAM - global.CURRENT_BOT_REPO = process.env.BOT + '-' + process.env.TYPE + '-Bot' + global.DEV_TEAM = process.env.DEV_TEAM + global.CURRENT_BOT_REPO = process.env.BOT + "-" + process.env.TYPE + "-Bot" - if (process.env.TYPE === 'Trading' || process.env.TYPE === 'Trading-Engine') { - let live = { - run: 'false', - resumeExecution: process.env.RESUME_EXECUTION, - beginDatetime: initialDatetime, - endDatetime: finalDatetime - } + if (process.env.TYPE === 'Trading') { + let live = { + run: 'false', + resumeExecution: process.env.RESUME_EXECUTION, + beginDatetime: initialDatetime, + endDatetime: finalDatetime + } - let backtest = { - run: 'false', - resumeExecution: process.env.RESUME_EXECUTION, - beginDatetime: initialDatetime, - endDatetime: finalDatetime - } + let backtest = { + run: 'false', + resumeExecution: process.env.RESUME_EXECUTION, + beginDatetime: initialDatetime, + endDatetime: finalDatetime + } - let competition = { - run: 'false', - resumeExecution: process.env.RESUME_EXECUTION, - beginDatetime: initialDatetime, - endDatetime: finalDatetime - } + let competition = { + run: 'false', + resumeExecution: process.env.RESUME_EXECUTION, + beginDatetime: initialDatetime, + endDatetime: finalDatetime + } - startMode = { - live: live, - backtest: backtest, - competition: competition - } - } else if (process.env.TYPE === 'Indicator' || process.env.TYPE === 'Sensor') { - let allMonths = { - run: 'false', - minYear: process.env.MIN_YEAR, - maxYear: process.env.MAX_YEAR - } - let oneMonth = { - run: 'false', - year: process.env.MIN_YEAR, - month: process.env.MONTH - } - let noTime = { - run: 'false', - beginDatetime: process.env.BEGIN_DATE_TIME, - resumeExecution: process.env.RESUME_EXECUTION - } - let fixedInterval = { - run: 'false', - interval: process.env.INTERVAL - } + startMode = { + live: live, + backtest: backtest, + competition: competition + } + } else if (process.env.TYPE === 'Indicator' || process.env.TYPE === 'Sensor') { + let allMonths = { + run: "false", + minYear: process.env.MIN_YEAR, + maxYear: process.env.MAX_YEAR + } + let oneMonth = { + run: "false", + year: process.env.MIN_YEAR, + month: process.env.MONTH + } + let noTime = { + run: "false", + beginDatetime: process.env.BEGIN_DATE_TIME, + resumeExecution: process.env.RESUME_EXECUTION + } + let fixedInterval = { + run: "false", + interval: process.env.INTERVAL + } - startMode = { - allMonths: allMonths, - oneMonth: oneMonth, - noTime: noTime, - fixedInterval: fixedInterval - } - } else { - console.log('[ERROR] readExecutionConfiguration -> Bot Type is invalid.') - throw new Error('readExecutionConfiguration -> Bot Type is invalid.') - } + startMode = { + allMonths: allMonths, + oneMonth: oneMonth, + noTime: noTime, + fixedInterval: fixedInterval + } + } else { + console.log("[ERROR] readExecutionConfiguration -> Bot Type is invalid."); + throw new Error("readExecutionConfiguration -> Bot Type is invalid.") + } - startMode[process.env.START_MODE].run = 'true' + startMode[process.env.START_MODE].run = "true" - let cloneToExecute = { - enabled: 'true', - devTeam: process.env.DEV_TEAM, - bot: process.env.BOT, - process: process.env.PROCESS, - repo: global.CURRENT_BOT_REPO + let cloneToExecute = { + enabled: "true", + devTeam: process.env.DEV_TEAM, + bot: process.env.BOT, + process: process.env.PROCESS, + repo: global.CURRENT_BOT_REPO + } + + global.EXECUTION_CONFIG = { + cloneToExecute: cloneToExecute, + startMode: startMode, + timePeriod: getTimePeriod(process.env.TIME_PERIOD), + timePeriodFileStorage: process.env.TIME_PERIOD, + dataSet: process.env.DATA_SET + }; + + global.CLONE_EXECUTOR = { + codeName: 'AACloud', + version: '1.1' + } + + startRoot(); } catch (err) { @@ -202,20 +219,9 @@ async function readExecutionConfiguration () { console.log("[ERROR] readExecutionConfiguration -> Please verify that the Start Mode for the type of Bot configured applies to that type."); console.log("[ERROR] readExecutionConfiguration -> err = " + err.stack); } - - global.CLONE_EXECUTOR = { - codeName: 'AACloud', - version: '1.1' - } - - startRoot() - } catch (err) { - console.log('[ERROR] readExecutionConfiguration -> err = ' + err) - console.log('[ERROR] readExecutionConfiguration -> Please verify that the Start Mode for the type of Bot configured applies to that type.') - console.log('[ERROR] readExecutionConfiguration -> err = ' + err.stack) - } } + function getTimePeriod (timePeriod) { if (timePeriod !== undefined) { try { From dc472da8960d6b51dda356907e9c097bcd0e6e79 Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Fri, 9 Aug 2019 01:19:47 +0200 Subject: [PATCH 15/19] Minor Improvements - All AAMaster sequence running. --- IndicatorBotProcessMainLoop.js | 2 +- Integrations/FileStorage.js | 7 +- Root.js | 4 +- sequence - all processes.json | 25 +++--- sequence.json | 144 ++++++++++++++++++++++++++++++--- 5 files changed, 152 insertions(+), 30 deletions(-) diff --git a/IndicatorBotProcessMainLoop.js b/IndicatorBotProcessMainLoop.js index bee07beff..93911b2db 100644 --- a/IndicatorBotProcessMainLoop.js +++ b/IndicatorBotProcessMainLoop.js @@ -433,7 +433,7 @@ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> startUserBot -> Entering function."); } - usertBot.start(onFinished); + usertBot.start(onFinished); function onFinished(err) { diff --git a/Integrations/FileStorage.js b/Integrations/FileStorage.js index 04d55c12a..5f9c7ce03 100644 --- a/Integrations/FileStorage.js +++ b/Integrations/FileStorage.js @@ -45,13 +45,16 @@ exports.newFileStorage = function newFileStorage() { } async function getTextFile(container, filePath, callBackFunction) { - logInfo('getTextFile: ' + container.toLowerCase() + '/...' + filePath.substring(filePath.length - 110, filePath.length)) + try { let host = await getDevTeamHost(container) if (host.url.indexOf('localhost') !== -1) { - let fileLocation = process.env.STORAGE_PATH + '/' + container + '/' + filePath + let fileLocation = process.env.STORAGE_PATH + '/' + container + '/' + filePath + + logInfo('getTextFile: ' + fileLocation) + let fileContent = await readFileAsync(fileLocation) callBackFunction(global.DEFAULT_OK_RESPONSE, fileContent.toString()) } else { diff --git a/Root.js b/Root.js index 65196deaa..84e3d4c8a 100644 --- a/Root.js +++ b/Root.js @@ -176,7 +176,7 @@ function onFileReceived(err, text) { if (err.result !== global.DEFAULT_OK_RESPONSE.result) { - console.log(logDisplace + "Root : [ERROR] start -> getBotConfig -> onInizialized -> onFileReceived -> err = "+ err); + console.log(logDisplace + "Root : [ERROR] start -> getBotConfig -> onInizialized -> onFileReceived -> err = " + JSON.stringify(err)); return; } @@ -186,7 +186,7 @@ botConfig.repo = cloneToExecute.repo; findProcess(); } catch (err) { - console.log(logDisplace + "Root : [ERROR] start -> getBotConfig -> onInizialized -> onFileReceived -> err = "+ err); + console.log(logDisplace + "Root : [ERROR] start -> getBotConfig -> onInizialized -> onFileReceived -> err = " + JSON.stringify(err)); return; } } diff --git a/sequence - all processes.json b/sequence - all processes.json index 740841b6a..9e39d1eac 100644 --- a/sequence - all processes.json +++ b/sequence - all processes.json @@ -23,7 +23,6 @@ "endYear": 2021, "month": 1 }, - { "devTeam": "AAMasters", "bot": "AABruce", @@ -108,18 +107,18 @@ "endYear": 2021, "month": 1 }, - { - "devTeam": "AAMasters", - "bot": "AAPaula", - "mode": "noTime", - "resumeExecution": true, - "type": "Sensor", - "exchangeName": "Poloniex", - "process": "Live-Trades", - "startYear": 2019, - "endYear": 2021, - "month": 1 - }, + { + "devTeam": "AAMasters", + "bot": "AAPaula", + "mode": "noTime", + "resumeExecution": true, + "type": "Indicator", + "exchangeName": "Poloniex", + "process": "Multi-Period-Daily", + "startYear": 2019, + "endYear": 2021, + "month": 1 + }, { "devTeam": "AAMasters", "bot": "AAPaula", diff --git a/sequence.json b/sequence.json index a0612a964..d86893a32 100644 --- a/sequence.json +++ b/sequence.json @@ -1,14 +1,134 @@ [ - { - "devTeam": "AAMasters", - "bot": "AACharly", - "mode": "noTime", - "resumeExecution": true, - "type": "Sensor", - "exchangeName": "Poloniex", - "process": "Live-Trades", - "startYear": 2019, - "endYear": 2021, - "month": 1 - } + { + "devTeam": "AAMasters", + "bot": "AACharly", + "mode": "noTime", + "resumeExecution": true, + "type": "Sensor", + "exchangeName": "Poloniex", + "process": "Live-Trades", + "startYear": 2019, + "endYear": 2021, + "month": 1 + }, + { + "devTeam": "AAMasters", + "bot": "AACharly", + "mode": "noTime", + "resumeExecution": true, + "type": "Sensor", + "exchangeName": "Poloniex", + "process": "Hole-Fixing", + "startYear": 2019, + "endYear": 2021, + "month": 1 + }, + { + "devTeam": "AAMasters", + "bot": "AABruce", + "mode": "noTime", + "resumeExecution": true, + "type": "Indicator", + "exchangeName": "Poloniex", + "process": "Single-Period-Daily", + "startYear": 2019, + "endYear": 2021, + "month": 1 + }, + { + "devTeam": "AAMasters", + "bot": "AAOlivia", + "mode": "noTime", + "resumeExecution": true, + "type": "Indicator", + "exchangeName": "Poloniex", + "process": "Multi-Period-Daily", + "startYear": 2019, + "endYear": 2021, + "month": 1 + }, + { + "devTeam": "AAMasters", + "bot": "AAOlivia", + "mode": "noTime", + "resumeExecution": true, + "type": "Indicator", + "exchangeName": "Poloniex", + "process": "Multi-Period-Market", + "startYear": 2019, + "endYear": 2021, + "month": 1 + }, + { + "devTeam": "AAMasters", + "bot": "AATom", + "mode": "noTime", + "resumeExecution": true, + "type": "Indicator", + "exchangeName": "Poloniex", + "process": "Multi-Period-Daily", + "startYear": 2019, + "endYear": 2021, + "month": 1 + }, + { + "devTeam": "AAMasters", + "bot": "AATom", + "mode": "noTime", + "resumeExecution": true, + "type": "Indicator", + "exchangeName": "Poloniex", + "process": "Multi-Period-Market", + "startYear": 2019, + "endYear": 2021, + "month": 1 + }, + { + "devTeam": "AAMasters", + "bot": "AAChris", + "mode": "noTime", + "resumeExecution": true, + "type": "Indicator", + "exchangeName": "Poloniex", + "process": "Multi-Period-Daily", + "startYear": 2019, + "endYear": 2021, + "month": 1 + }, + { + "devTeam": "AAMasters", + "bot": "AAChris", + "mode": "noTime", + "resumeExecution": true, + "type": "Indicator", + "exchangeName": "Poloniex", + "process": "Multi-Period-Market", + "startYear": 2019, + "endYear": 2021, + "month": 1 + }, + { + "devTeam": "AAMasters", + "bot": "AAPaula", + "mode": "noTime", + "resumeExecution": true, + "type": "Indicator", + "exchangeName": "Poloniex", + "process": "Multi-Period-Daily", + "startYear": 2019, + "endYear": 2021, + "month": 1 + }, + { + "devTeam": "AAMasters", + "bot": "AAPaula", + "mode": "noTime", + "resumeExecution": true, + "type": "Indicator", + "exchangeName": "Poloniex", + "process": "Multi-Period-Market", + "startYear": 2019, + "endYear": 2021, + "month": 1 + } ] From ab9e9e55af68f86e87548e84d5ac99dc8481f3d0 Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Fri, 9 Aug 2019 16:21:48 +0200 Subject: [PATCH 16/19] Simulator added to sequence --- sequence - all processes.json | 321 ++++++++++++++++++---------------- 1 file changed, 166 insertions(+), 155 deletions(-) diff --git a/sequence - all processes.json b/sequence - all processes.json index 9e39d1eac..18933d9c7 100644 --- a/sequence - all processes.json +++ b/sequence - all processes.json @@ -1,112 +1,112 @@ [ - { - "devTeam": "AAMasters", - "bot": "AACharly", - "mode": "noTime", - "resumeExecution": true, - "type": "Sensor", - "exchangeName": "Poloniex", - "process": "Live-Trades", - "startYear": 2019, - "endYear": 2021, - "month": 1 - }, - { - "devTeam": "AAMasters", - "bot": "AACharly", - "mode": "noTime", - "resumeExecution": true, - "type": "Sensor", - "exchangeName": "Poloniex", - "process": "Hole-Fixing", - "startYear": 2019, - "endYear": 2021, - "month": 1 - }, - { - "devTeam": "AAMasters", - "bot": "AABruce", - "mode": "noTime", - "resumeExecution": true, - "type": "Indicator", - "exchangeName": "Poloniex", - "process": "Single-Period-Daily", - "startYear": 2019, - "endYear": 2021, - "month": 1 - }, - { - "devTeam": "AAMasters", - "bot": "AAOlivia", - "mode": "noTime", - "resumeExecution": true, - "type": "Indicator", - "exchangeName": "Poloniex", - "process": "Multi-Period-Daily", - "startYear": 2019, - "endYear": 2021, - "month": 1 - }, - { - "devTeam": "AAMasters", - "bot": "AAOlivia", - "mode": "noTime", - "resumeExecution": true, - "type": "Indicator", - "exchangeName": "Poloniex", - "process": "Multi-Period-Market", - "startYear": 2019, - "endYear": 2021, - "month": 1 - }, - { - "devTeam": "AAMasters", - "bot": "AATom", - "mode": "noTime", - "resumeExecution": true, - "type": "Indicator", - "exchangeName": "Poloniex", - "process": "Multi-Period-Daily", - "startYear": 2019, - "endYear": 2021, - "month": 1 - }, - { - "devTeam": "AAMasters", - "bot": "AATom", - "mode": "noTime", - "resumeExecution": true, - "type": "Indicator", - "exchangeName": "Poloniex", - "process": "Multi-Period-Market", - "startYear": 2019, - "endYear": 2021, - "month": 1 - }, - { - "devTeam": "AAMasters", - "bot": "AAChris", - "mode": "noTime", - "resumeExecution": true, - "type": "Indicator", - "exchangeName": "Poloniex", - "process": "Multi-Period-Daily", - "startYear": 2019, - "endYear": 2021, - "month": 1 - }, - { - "devTeam": "AAMasters", - "bot": "AAChris", - "mode": "noTime", - "resumeExecution": true, - "type": "Indicator", - "exchangeName": "Poloniex", - "process": "Multi-Period-Market", - "startYear": 2019, - "endYear": 2021, - "month": 1 - }, + { + "devTeam": "AAMasters", + "bot": "AACharly", + "mode": "noTime", + "resumeExecution": true, + "type": "Sensor", + "exchangeName": "Poloniex", + "process": "Live-Trades", + "startYear": 2019, + "endYear": 2021, + "month": 1 + }, + { + "devTeam": "AAMasters", + "bot": "AACharly", + "mode": "noTime", + "resumeExecution": true, + "type": "Sensor", + "exchangeName": "Poloniex", + "process": "Hole-Fixing", + "startYear": 2019, + "endYear": 2021, + "month": 1 + }, + { + "devTeam": "AAMasters", + "bot": "AABruce", + "mode": "noTime", + "resumeExecution": true, + "type": "Indicator", + "exchangeName": "Poloniex", + "process": "Single-Period-Daily", + "startYear": 2019, + "endYear": 2021, + "month": 1 + }, + { + "devTeam": "AAMasters", + "bot": "AAOlivia", + "mode": "noTime", + "resumeExecution": true, + "type": "Indicator", + "exchangeName": "Poloniex", + "process": "Multi-Period-Daily", + "startYear": 2019, + "endYear": 2021, + "month": 1 + }, + { + "devTeam": "AAMasters", + "bot": "AAOlivia", + "mode": "noTime", + "resumeExecution": true, + "type": "Indicator", + "exchangeName": "Poloniex", + "process": "Multi-Period-Market", + "startYear": 2019, + "endYear": 2021, + "month": 1 + }, + { + "devTeam": "AAMasters", + "bot": "AATom", + "mode": "noTime", + "resumeExecution": true, + "type": "Indicator", + "exchangeName": "Poloniex", + "process": "Multi-Period-Daily", + "startYear": 2019, + "endYear": 2021, + "month": 1 + }, + { + "devTeam": "AAMasters", + "bot": "AATom", + "mode": "noTime", + "resumeExecution": true, + "type": "Indicator", + "exchangeName": "Poloniex", + "process": "Multi-Period-Market", + "startYear": 2019, + "endYear": 2021, + "month": 1 + }, + { + "devTeam": "AAMasters", + "bot": "AAChris", + "mode": "noTime", + "resumeExecution": true, + "type": "Indicator", + "exchangeName": "Poloniex", + "process": "Multi-Period-Daily", + "startYear": 2019, + "endYear": 2021, + "month": 1 + }, + { + "devTeam": "AAMasters", + "bot": "AAChris", + "mode": "noTime", + "resumeExecution": true, + "type": "Indicator", + "exchangeName": "Poloniex", + "process": "Multi-Period-Market", + "startYear": 2019, + "endYear": 2021, + "month": 1 + }, { "devTeam": "AAMasters", "bot": "AAPaula", @@ -119,30 +119,30 @@ "endYear": 2021, "month": 1 }, - { - "devTeam": "AAMasters", - "bot": "AAPaula", - "mode": "noTime", - "resumeExecution": true, - "type": "Indicator", - "exchangeName": "Poloniex", - "process": "Multi-Period-Market", - "startYear": 2019, - "endYear": 2021, - "month": 1 - }, - { - "devTeam": "AAVikings", - "bot": "AAGauss", - "mode": "noTime", - "resumeExecution": true, - "type": "Indicator", - "exchangeName": "Poloniex", - "process": "Multi-Period-Daily", - "startYear": 2019, - "endYear": 2021, - "month": 1 - }, + { + "devTeam": "AAMasters", + "bot": "AAPaula", + "mode": "noTime", + "resumeExecution": true, + "type": "Indicator", + "exchangeName": "Poloniex", + "process": "Multi-Period-Market", + "startYear": 2019, + "endYear": 2021, + "month": 1 + }, + { + "devTeam": "AAVikings", + "bot": "AAGauss", + "mode": "noTime", + "resumeExecution": true, + "type": "Indicator", + "exchangeName": "Poloniex", + "process": "Multi-Period-Daily", + "startYear": 2019, + "endYear": 2021, + "month": 1 + }, { "devTeam": "AAVikings", "bot": "AAGauss", @@ -155,27 +155,38 @@ "endYear": 2021, "month": 1 }, - { - "devTeam": "vikingsalerts", - "bot": "simulator-bot-vikingsalerts", - "mode": "noTime", - "resumeExecution": true, - "type": "Indicator", - "exchangeName": "Poloniex", - "process": "Multi-Period-Daily", - "beginDatetime": "2019-08-01T08:00:00.000Z", - "timePeriod": "05-min" - }, - { - "devTeam": "vikingsalerts", - "bot": "bot-vikingsalerts", - "mode": "live", - "resumeExecution": true, - "type": "Trading", - "exchangeName": "Poloniex", - "process": "Trading-Process", - "beginDatetime": "2019-08-01T08:00:00.000Z", - "timePeriod": "05-min", - "dataSet": "Multi-Period-Daily" - } + { + "devTeam": "vikingsalerts", + "bot": "simulator-bot-vikingsalerts", + "mode": "noTime", + "resumeExecution": true, + "type": "Indicator", + "exchangeName": "Poloniex", + "process": "Multi-Period-Daily", + "beginDatetime": "2019-08-01T08:00:00.000Z", + "timePeriod": "05-min" + }, + { + "devTeam": "vikingsalerts", + "bot": "bot-vikingsalerts", + "mode": "live", + "resumeExecution": true, + "type": "Trading", + "exchangeName": "Poloniex", + "process": "Trading-Process", + "beginDatetime": "2019-08-01T08:00:00.000Z", + "timePeriod": "05-min", + "dataSet": "Multi-Period-Daily" + }, + { + "devTeam": "user", + "bot": "simulator-bot-user", + "mode": "noTime", + "resumeExecution": true, + "type": "Indicator", + "exchangeName": "Poloniex", + "process": "Multi-Period-Daily", + "beginDatetime": "2019-08-01T08:00:00.000Z", + "timePeriod": "05-min" + } ] From ccbc1665f456a390e355aca29579008bbb783237 Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Fri, 9 Aug 2019 17:39:37 +0200 Subject: [PATCH 17/19] Trading Engine in sequence --- Context.js | 2 +- Root.js | 1 + Run.js | 3 +- TradingEngineProcessMainLoop.js | 2 + sequence - all processes.json | 22 +++--- sequence.json | 132 ++------------------------------ 6 files changed, 22 insertions(+), 140 deletions(-) diff --git a/Context.js b/Context.js index 5bee4f029..19742d5db 100644 --- a/Context.js +++ b/Context.js @@ -170,7 +170,7 @@ function onFileReceived(err, text) { if (err.result !== global.DEFAULT_OK_RESPONSE.result) { - logger.write(MODULE_NAME, "[ERROR] initialize -> getExecutionHistory -> onFileReceived -> err = "+ err); + logger.write(MODULE_NAME, "[ERROR] initialize -> getExecutionHistory -> onFileReceived -> err = "+ JSON.stringify(err)); callBack(err); return; } diff --git a/Root.js b/Root.js index 84e3d4c8a..657b41f65 100644 --- a/Root.js +++ b/Root.js @@ -857,6 +857,7 @@ console.log(logDisplace + "Root : [ERROR] start -> findProcess -> runTradingEngine -> onInitializeReady -> whenStartFinishes -> Bot execution finished with errors. Please check the logs."); logger.persist(); } + callback(err, pBotConfig.devTeam + "." + pBotConfig.codeName + "." + pBotConfig.process) } } else { diff --git a/Run.js b/Run.js index 5c97d04c6..9f8207c7d 100644 --- a/Run.js +++ b/Run.js @@ -1,6 +1,5 @@ require('dotenv').config() const strategy = require('./Integrations/Strategy') -const sequenceList = require('./sequence'); global.SHALL_BOT_STOP = false global.AT_BREAKPOINT = false // This is used only when running at the browser. @@ -156,7 +155,7 @@ async function readExecutionConfiguration() { backtest: backtest, competition: competition } - } else if (process.env.TYPE === 'Indicator' || process.env.TYPE === 'Sensor') { + } else if (process.env.TYPE === 'Indicator' || process.env.TYPE === 'Sensor' || process.env.TYPE === 'Trading-Engine') { let allMonths = { run: "false", minYear: process.env.MIN_YEAR, diff --git a/TradingEngineProcessMainLoop.js b/TradingEngineProcessMainLoop.js index 6c70bf3af..de6e93245 100644 --- a/TradingEngineProcessMainLoop.js +++ b/TradingEngineProcessMainLoop.js @@ -36,6 +36,8 @@ try { if (FULL_LOG === true) { parentLogger.write(MODULE_NAME, "[INFO] initialize -> Entering function."); } + bot.startMode = 'Live' // The Trading Engine does not support different start modes. Only live for background compatibility. + UI_COMMANDS = pUI_COMMANDS; processConfig = pProcessConfig; diff --git a/sequence - all processes.json b/sequence - all processes.json index 18933d9c7..e98ae87fd 100644 --- a/sequence - all processes.json +++ b/sequence - all processes.json @@ -131,6 +131,17 @@ "endYear": 2021, "month": 1 }, + { + "devTeam": "user", + "bot": "simulator-user", + "mode": "noTime", + "resumeExecution": true, + "type": "Trading-Engine", + "exchangeName": "Poloniex", + "process": "Multi-Period-Daily", + "beginDatetime": "2019-08-01T08:00:00.000Z", + "timePeriod": "05-min" + }, { "devTeam": "AAVikings", "bot": "AAGauss", @@ -177,16 +188,5 @@ "beginDatetime": "2019-08-01T08:00:00.000Z", "timePeriod": "05-min", "dataSet": "Multi-Period-Daily" - }, - { - "devTeam": "user", - "bot": "simulator-bot-user", - "mode": "noTime", - "resumeExecution": true, - "type": "Indicator", - "exchangeName": "Poloniex", - "process": "Multi-Period-Daily", - "beginDatetime": "2019-08-01T08:00:00.000Z", - "timePeriod": "05-min" } ] diff --git a/sequence.json b/sequence.json index d86893a32..3ee401586 100644 --- a/sequence.json +++ b/sequence.json @@ -1,134 +1,14 @@ [ + { - "devTeam": "AAMasters", - "bot": "AACharly", + "devTeam": "user", + "bot": "simulator-user", "mode": "noTime", "resumeExecution": true, - "type": "Sensor", - "exchangeName": "Poloniex", - "process": "Live-Trades", - "startYear": 2019, - "endYear": 2021, - "month": 1 - }, - { - "devTeam": "AAMasters", - "bot": "AACharly", - "mode": "noTime", - "resumeExecution": true, - "type": "Sensor", - "exchangeName": "Poloniex", - "process": "Hole-Fixing", - "startYear": 2019, - "endYear": 2021, - "month": 1 - }, - { - "devTeam": "AAMasters", - "bot": "AABruce", - "mode": "noTime", - "resumeExecution": true, - "type": "Indicator", - "exchangeName": "Poloniex", - "process": "Single-Period-Daily", - "startYear": 2019, - "endYear": 2021, - "month": 1 - }, - { - "devTeam": "AAMasters", - "bot": "AAOlivia", - "mode": "noTime", - "resumeExecution": true, - "type": "Indicator", + "type": "Trading-Engine", "exchangeName": "Poloniex", "process": "Multi-Period-Daily", - "startYear": 2019, - "endYear": 2021, - "month": 1 - }, - { - "devTeam": "AAMasters", - "bot": "AAOlivia", - "mode": "noTime", - "resumeExecution": true, - "type": "Indicator", - "exchangeName": "Poloniex", - "process": "Multi-Period-Market", - "startYear": 2019, - "endYear": 2021, - "month": 1 - }, - { - "devTeam": "AAMasters", - "bot": "AATom", - "mode": "noTime", - "resumeExecution": true, - "type": "Indicator", - "exchangeName": "Poloniex", - "process": "Multi-Period-Daily", - "startYear": 2019, - "endYear": 2021, - "month": 1 - }, - { - "devTeam": "AAMasters", - "bot": "AATom", - "mode": "noTime", - "resumeExecution": true, - "type": "Indicator", - "exchangeName": "Poloniex", - "process": "Multi-Period-Market", - "startYear": 2019, - "endYear": 2021, - "month": 1 - }, - { - "devTeam": "AAMasters", - "bot": "AAChris", - "mode": "noTime", - "resumeExecution": true, - "type": "Indicator", - "exchangeName": "Poloniex", - "process": "Multi-Period-Daily", - "startYear": 2019, - "endYear": 2021, - "month": 1 - }, - { - "devTeam": "AAMasters", - "bot": "AAChris", - "mode": "noTime", - "resumeExecution": true, - "type": "Indicator", - "exchangeName": "Poloniex", - "process": "Multi-Period-Market", - "startYear": 2019, - "endYear": 2021, - "month": 1 - }, - { - "devTeam": "AAMasters", - "bot": "AAPaula", - "mode": "noTime", - "resumeExecution": true, - "type": "Indicator", - "exchangeName": "Poloniex", - "process": "Multi-Period-Daily", - "startYear": 2019, - "endYear": 2021, - "month": 1 - }, - { - "devTeam": "AAMasters", - "bot": "AAPaula", - "mode": "noTime", - "resumeExecution": true, - "type": "Indicator", - "exchangeName": "Poloniex", - "process": "Multi-Period-Market", - "startYear": 2019, - "endYear": 2021, - "month": 1 + "beginDatetime": "2019-08-01T08:00:00.000Z", + "timePeriod": "05-min" } ] From f389a57bad8d0c50b556024b42067e56b2dfa59a Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Fri, 9 Aug 2019 19:29:05 +0200 Subject: [PATCH 18/19] Jason Running Sequenced --- Run.js | 4 ++-- User.Bot.js | 14 +++++++------- sequence - all processes.json | 4 ++-- sequence.json | 6 +++--- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/Run.js b/Run.js index 9f8207c7d..dcaa1f688 100644 --- a/Run.js +++ b/Run.js @@ -128,7 +128,7 @@ async function readExecutionConfiguration() { global.DEV_TEAM = process.env.DEV_TEAM global.CURRENT_BOT_REPO = process.env.BOT + "-" + process.env.TYPE + "-Bot" - if (process.env.TYPE === 'Trading') { + if (process.env.TYPE === 'Trading' || process.env.TYPE === 'Trading-Engine') { let live = { run: 'false', resumeExecution: process.env.RESUME_EXECUTION, @@ -155,7 +155,7 @@ async function readExecutionConfiguration() { backtest: backtest, competition: competition } - } else if (process.env.TYPE === 'Indicator' || process.env.TYPE === 'Sensor' || process.env.TYPE === 'Trading-Engine') { + } else if (process.env.TYPE === 'Indicator' || process.env.TYPE === 'Sensor') { let allMonths = { run: "false", minYear: process.env.MIN_YEAR, diff --git a/User.Bot.js b/User.Bot.js index 1e457f5a7..ca0130408 100644 --- a/User.Bot.js +++ b/User.Bot.js @@ -70,28 +70,28 @@ dataFile = dataFiles[i]; switch (i) { - + /* case 0: { commons.buildLRC(dataFile, callBackFunction); break; - } - case 1: { + }*/ + case 0: { commons.buildPercentageBandwidthMap(dataFile, callBackFunction); break; } - case 2: { + case 1: { commons.buildBollingerBandsMap(dataFile, callBackFunction); break; } - case 3: { + case 2: { commons.buildBollingerChannelsArray(dataFile, callBackFunction); break; } - case 4: { + case 3: { commons.buildBollingerSubChannelsArray(dataFile, callBackFunction); break; } - case 5: { + case 4: { commons.buildCandles(dataFile, callBackFunction); break; } diff --git a/sequence - all processes.json b/sequence - all processes.json index e98ae87fd..babf96089 100644 --- a/sequence - all processes.json +++ b/sequence - all processes.json @@ -132,8 +132,8 @@ "month": 1 }, { - "devTeam": "user", - "bot": "simulator-user", + "devTeam": "AAMasters", + "bot": "AAJason", "mode": "noTime", "resumeExecution": true, "type": "Trading-Engine", diff --git a/sequence.json b/sequence.json index 3ee401586..076ac7d19 100644 --- a/sequence.json +++ b/sequence.json @@ -1,9 +1,9 @@ [ { - "devTeam": "user", - "bot": "simulator-user", - "mode": "noTime", + "devTeam": "AAMasters", + "bot": "AAJason", + "mode": "live", "resumeExecution": true, "type": "Trading-Engine", "exchangeName": "Poloniex", From 958b92be7a50dcd83d82ce5898a1f41b15950d1a Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Fri, 9 Aug 2019 19:56:54 +0200 Subject: [PATCH 19/19] All process running including trading engine --- sequence - all processes.json | 2 +- sequence.json | 133 +++++++++++++++++++++++++++++++++- 2 files changed, 133 insertions(+), 2 deletions(-) diff --git a/sequence - all processes.json b/sequence - all processes.json index babf96089..20650bd26 100644 --- a/sequence - all processes.json +++ b/sequence - all processes.json @@ -134,7 +134,7 @@ { "devTeam": "AAMasters", "bot": "AAJason", - "mode": "noTime", + "mode": "live", "resumeExecution": true, "type": "Trading-Engine", "exchangeName": "Poloniex", diff --git a/sequence.json b/sequence.json index 076ac7d19..10ca0ec47 100644 --- a/sequence.json +++ b/sequence.json @@ -1,5 +1,136 @@ [ - + { + "devTeam": "AAMasters", + "bot": "AACharly", + "mode": "noTime", + "resumeExecution": true, + "type": "Sensor", + "exchangeName": "Poloniex", + "process": "Live-Trades", + "startYear": 2019, + "endYear": 2021, + "month": 1 + }, + { + "devTeam": "AAMasters", + "bot": "AACharly", + "mode": "noTime", + "resumeExecution": true, + "type": "Sensor", + "exchangeName": "Poloniex", + "process": "Hole-Fixing", + "startYear": 2019, + "endYear": 2021, + "month": 1 + }, + { + "devTeam": "AAMasters", + "bot": "AABruce", + "mode": "noTime", + "resumeExecution": true, + "type": "Indicator", + "exchangeName": "Poloniex", + "process": "Single-Period-Daily", + "startYear": 2019, + "endYear": 2021, + "month": 1 + }, + { + "devTeam": "AAMasters", + "bot": "AAOlivia", + "mode": "noTime", + "resumeExecution": true, + "type": "Indicator", + "exchangeName": "Poloniex", + "process": "Multi-Period-Daily", + "startYear": 2019, + "endYear": 2021, + "month": 1 + }, + { + "devTeam": "AAMasters", + "bot": "AAOlivia", + "mode": "noTime", + "resumeExecution": true, + "type": "Indicator", + "exchangeName": "Poloniex", + "process": "Multi-Period-Market", + "startYear": 2019, + "endYear": 2021, + "month": 1 + }, + { + "devTeam": "AAMasters", + "bot": "AATom", + "mode": "noTime", + "resumeExecution": true, + "type": "Indicator", + "exchangeName": "Poloniex", + "process": "Multi-Period-Daily", + "startYear": 2019, + "endYear": 2021, + "month": 1 + }, + { + "devTeam": "AAMasters", + "bot": "AATom", + "mode": "noTime", + "resumeExecution": true, + "type": "Indicator", + "exchangeName": "Poloniex", + "process": "Multi-Period-Market", + "startYear": 2019, + "endYear": 2021, + "month": 1 + }, + { + "devTeam": "AAMasters", + "bot": "AAChris", + "mode": "noTime", + "resumeExecution": true, + "type": "Indicator", + "exchangeName": "Poloniex", + "process": "Multi-Period-Daily", + "startYear": 2019, + "endYear": 2021, + "month": 1 + }, + { + "devTeam": "AAMasters", + "bot": "AAChris", + "mode": "noTime", + "resumeExecution": true, + "type": "Indicator", + "exchangeName": "Poloniex", + "process": "Multi-Period-Market", + "startYear": 2019, + "endYear": 2021, + "month": 1 + }, + { + "devTeam": "AAMasters", + "bot": "AAPaula", + "mode": "noTime", + "resumeExecution": true, + "type": "Indicator", + "exchangeName": "Poloniex", + "process": "Multi-Period-Daily", + "startYear": 2019, + "endYear": 2021, + "month": 1 + }, + { + "devTeam": "AAMasters", + "bot": "AAPaula", + "mode": "noTime", + "resumeExecution": true, + "type": "Indicator", + "exchangeName": "Poloniex", + "process": "Multi-Period-Market", + "startYear": 2019, + "endYear": 2021, + "month": 1 + }, { "devTeam": "AAMasters", "bot": "AAJason",