diff --git a/.travis.yml b/.travis.yml index e9a7dd52cc..6c6e7f05a9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,7 @@ language: node_js -node_js: node +node_js: "10.15.3" +before_install: npm i -g npm@6.9.0 +install: npm ci cache: directories: - node_modules diff --git a/package-lock.json b/package-lock.json index 9efde45659..a47d191ce6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9832,9 +9832,9 @@ } }, "handlebars": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.1.1.tgz", - "integrity": "sha512-3Zhi6C0euYZL5sM0Zcy7lInLXKQ+YLcF/olbN010mzGQ4XVm50JeyBnMqofHh696GrciGruC7kCcApPDJvVgwA==", + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.1.2.tgz", + "integrity": "sha512-nvfrjqvt9xQ8Z/w0ijewdD/vvWDTOweBUm96NTr66Wfvo1mJenBLwcYmPs3TIBP5ruzYGD7Hx/DaM9RmhroGPw==", "dev": true, "requires": { "neo-async": "^2.6.0", @@ -10771,136 +10771,26 @@ "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" }, "istanbul-api": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/istanbul-api/-/istanbul-api-2.1.1.tgz", - "integrity": "sha512-kVmYrehiwyeBAk/wE71tW6emzLiHGjYIiDrc8sfyty4F8M02/lrgXSm+R1kXysmF20zArvmZXjlE/mg24TVPJw==", + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/istanbul-api/-/istanbul-api-2.1.6.tgz", + "integrity": "sha512-x0Eicp6KsShG1k1rMgBAi/1GgY7kFGEBwQpw3PXGEmu+rBcBNhqU8g2DgY9mlepAsLPzrzrbqSgCGANnki4POA==", "dev": true, "requires": { - "async": "^2.6.1", - "compare-versions": "^3.2.1", + "async": "^2.6.2", + "compare-versions": "^3.4.0", "fileset": "^2.0.3", - "istanbul-lib-coverage": "^2.0.3", - "istanbul-lib-hook": "^2.0.3", - "istanbul-lib-instrument": "^3.1.0", - "istanbul-lib-report": "^2.0.4", - "istanbul-lib-source-maps": "^3.0.2", - "istanbul-reports": "^2.1.1", - "js-yaml": "^3.12.0", - "make-dir": "^1.3.0", + "istanbul-lib-coverage": "^2.0.5", + "istanbul-lib-hook": "^2.0.7", + "istanbul-lib-instrument": "^3.3.0", + "istanbul-lib-report": "^2.0.8", + "istanbul-lib-source-maps": "^3.0.6", + "istanbul-reports": "^2.2.4", + "js-yaml": "^3.13.1", + "make-dir": "^2.1.0", "minimatch": "^3.0.4", "once": "^1.4.0" }, "dependencies": { - "@babel/code-frame": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.0.0.tgz", - "integrity": "sha512-OfC2uemaknXr87bdLUkWog7nYuliM9Ij5HUcajsVcMCpQrcLmtxRbVFTIqmcSkSeYRBFBRxs2FiUqFJDLdiebA==", - "dev": true, - "requires": { - "@babel/highlight": "^7.0.0" - } - }, - "@babel/generator": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.4.0.tgz", - "integrity": "sha512-/v5I+a1jhGSKLgZDcmAUZ4K/VePi43eRkUs3yePW1HB1iANOD5tqJXwGSG4BZhSksP8J9ejSlwGeTiiOFZOrXQ==", - "dev": true, - "requires": { - "@babel/types": "^7.4.0", - "jsesc": "^2.5.1", - "lodash": "^4.17.11", - "source-map": "^0.5.0", - "trim-right": "^1.0.1" - } - }, - "@babel/helper-function-name": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.1.0.tgz", - "integrity": "sha512-A95XEoCpb3TO+KZzJ4S/5uW5fNe26DjBGqf1o9ucyLyCmi1dXq/B3c8iaWTfBk3VvetUxl16e8tIrd5teOCfGw==", - "dev": true, - "requires": { - "@babel/helper-get-function-arity": "^7.0.0", - "@babel/template": "^7.1.0", - "@babel/types": "^7.0.0" - } - }, - "@babel/helper-get-function-arity": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.0.0.tgz", - "integrity": "sha512-r2DbJeg4svYvt3HOS74U4eWKsUAMRH01Z1ds1zx8KNTPtpTL5JAsdFv8BNyOpVqdFhHkkRDIg5B4AsxmkjAlmQ==", - "dev": true, - "requires": { - "@babel/types": "^7.0.0" - } - }, - "@babel/helper-split-export-declaration": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.4.0.tgz", - "integrity": "sha512-7Cuc6JZiYShaZnybDmfwhY4UYHzI6rlqhWjaIqbsJGsIqPimEYy5uh3akSRLMg65LSdSEnJ8a8/bWQN6u2oMGw==", - "dev": true, - "requires": { - "@babel/types": "^7.4.0" - } - }, - "@babel/highlight": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.0.0.tgz", - "integrity": "sha512-UFMC4ZeFC48Tpvj7C8UgLvtkaUuovQX+5xNWrsIoMG8o2z+XFKjKaN9iVmS84dPwVN00W4wPmqvYoZF3EGAsfw==", - "dev": true, - "requires": { - "chalk": "^2.0.0", - "esutils": "^2.0.2", - "js-tokens": "^4.0.0" - } - }, - "@babel/template": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.4.0.tgz", - "integrity": "sha512-SOWwxxClTTh5NdbbYZ0BmaBVzxzTh2tO/TeLTbF6MO6EzVhHTnff8CdBXx3mEtazFBoysmEM6GU/wF+SuSx4Fw==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "@babel/parser": "^7.4.0", - "@babel/types": "^7.4.0" - } - }, - "@babel/traverse": { - "version": "7.4.3", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.4.3.tgz", - "integrity": "sha512-HmA01qrtaCwwJWpSKpA948cBvU5BrmviAief/b3AVw936DtcdsTexlbyzNuDnthwhOQ37xshn7hvQaEQk7ISYQ==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "@babel/generator": "^7.4.0", - "@babel/helper-function-name": "^7.1.0", - "@babel/helper-split-export-declaration": "^7.4.0", - "@babel/parser": "^7.4.3", - "@babel/types": "^7.4.0", - "debug": "^4.1.0", - "globals": "^11.1.0", - "lodash": "^4.17.11" - } - }, - "@babel/types": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.4.0.tgz", - "integrity": "sha512-aPvkXyU2SPOnztlgo8n9cEiXW755mgyvueUPcpStqdzoSPm0fjO0vQBjLkt3JKJW7ufikfcnMTTPsN1xaTsBPA==", - "dev": true, - "requires": { - "esutils": "^2.0.2", - "lodash": "^4.17.11", - "to-fast-properties": "^2.0.0" - } - }, - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, "async": { "version": "2.6.2", "resolved": "https://registry.npmjs.org/async/-/async-2.6.2.tgz", @@ -10910,17 +10800,6 @@ "lodash": "^4.17.11" } }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, "debug": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", @@ -10930,38 +10809,57 @@ "ms": "^2.1.1" } }, - "globals": { - "version": "11.11.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.11.0.tgz", - "integrity": "sha512-WHq43gS+6ufNOEqlrDBxVEbb8ntfXrfAUU2ZOpCxrBdGKW3gyv8mCxAfIBD0DroPKGrJ2eSsXsLtY9MPntsyTw==", - "dev": true - }, "istanbul-lib-coverage": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz", - "integrity": "sha512-dKWuzRGCs4G+67VfW9pBFFz2Jpi4vSp/k7zBcJ888ofV5Mi1g5CUML5GvMvV6u9Cjybftu+E8Cgp+k0dI1E5lw==", + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.5.tgz", + "integrity": "sha512-8aXznuEPCJvGnMSRft4udDRDtb1V3pkQkMMI5LI+6HuQz5oQ4J2UFn1H82raA3qJtyOLkkwVqICBQkjnGtn5mA==", "dev": true }, "istanbul-lib-instrument": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-3.1.0.tgz", - "integrity": "sha512-ooVllVGT38HIk8MxDj/OIHXSYvH+1tq/Vb38s8ixt9GoJadXska4WkGY+0wkmtYCZNYtaARniH/DixUGGLZ0uA==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-3.3.0.tgz", + "integrity": "sha512-5nnIN4vo5xQZHdXno/YDXJ0G+I3dAm4XgzfSVTPLQpj/zAV2dV6Juy0yaf10/zrJOJeHoN3fraFe+XRq2bFVZA==", "dev": true, "requires": { - "@babel/generator": "^7.0.0", - "@babel/parser": "^7.0.0", - "@babel/template": "^7.0.0", - "@babel/traverse": "^7.0.0", - "@babel/types": "^7.0.0", - "istanbul-lib-coverage": "^2.0.3", - "semver": "^5.5.0" + "@babel/generator": "^7.4.0", + "@babel/parser": "^7.4.3", + "@babel/template": "^7.4.0", + "@babel/traverse": "^7.4.3", + "@babel/types": "^7.4.0", + "istanbul-lib-coverage": "^2.0.5", + "semver": "^6.0.0" } }, - "jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", - "dev": true + "istanbul-lib-source-maps": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-3.0.6.tgz", + "integrity": "sha512-R47KzMtDJH6X4/YW9XTx+jrLnZnscW4VpNN+1PViSYTejLVPWv7oov+Duf8YQSPyVRUvueQqz1TcsC6mooZTXw==", + "dev": true, + "requires": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^2.0.5", + "make-dir": "^2.1.0", + "rimraf": "^2.6.3", + "source-map": "^0.6.1" + } + }, + "make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "dev": true, + "requires": { + "pify": "^4.0.1", + "semver": "^5.6.0" + }, + "dependencies": { + "semver": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", + "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==", + "dev": true + } + } }, "ms": { "version": "2.1.1", @@ -10969,25 +10867,16 @@ "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", "dev": true }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", "dev": true }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - }, - "to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", + "semver": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.0.0.tgz", + "integrity": "sha512-0UewU+9rFapKFnlbirLi3byoOuhrSsli/z/ihNnvM24vgF+8sNBiI1LZPBSH9wJKUwaUbw+s3hToDLCXkrghrQ==", "dev": true } } @@ -10999,9 +10888,9 @@ "dev": true }, "istanbul-lib-hook": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-2.0.3.tgz", - "integrity": "sha512-CLmEqwEhuCYtGcpNVJjLV1DQyVnIqavMLFHV/DP+np/g3qvdxu3gsPqYoJMXm15sN84xOlckFB3VNvRbf5yEgA==", + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-2.0.7.tgz", + "integrity": "sha512-vrRztU9VRRFDyC+aklfLoeXyNdTfga2EI3udDGn4cZ6fpSXpHLV9X6CHvfoMCPtggg8zvDDmC4b9xfu0z6/llA==", "dev": true, "requires": { "append-transform": "^1.0.0" @@ -11023,20 +10912,36 @@ } }, "istanbul-lib-report": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-2.0.4.tgz", - "integrity": "sha512-sOiLZLAWpA0+3b5w5/dq0cjm2rrNdAfHWaGhmn7XEFW6X++IV9Ohn+pnELAl9K3rfpaeBfbmH9JU5sejacdLeA==", + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-2.0.8.tgz", + "integrity": "sha512-fHBeG573EIihhAblwgxrSenp0Dby6tJMFR/HvlerBsrCTD5bkUuoNtn3gVh29ZCS824cGGBPn7Sg7cNk+2xUsQ==", "dev": true, "requires": { - "istanbul-lib-coverage": "^2.0.3", - "make-dir": "^1.3.0", - "supports-color": "^6.0.0" + "istanbul-lib-coverage": "^2.0.5", + "make-dir": "^2.1.0", + "supports-color": "^6.1.0" }, "dependencies": { "istanbul-lib-coverage": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz", - "integrity": "sha512-dKWuzRGCs4G+67VfW9pBFFz2Jpi4vSp/k7zBcJ888ofV5Mi1g5CUML5GvMvV6u9Cjybftu+E8Cgp+k0dI1E5lw==", + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.5.tgz", + "integrity": "sha512-8aXznuEPCJvGnMSRft4udDRDtb1V3pkQkMMI5LI+6HuQz5oQ4J2UFn1H82raA3qJtyOLkkwVqICBQkjnGtn5mA==", + "dev": true + }, + "make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "dev": true, + "requires": { + "pify": "^4.0.1", + "semver": "^5.6.0" + } + }, + "pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", "dev": true }, "supports-color": { @@ -11087,12 +10992,12 @@ } }, "istanbul-reports": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-2.1.1.tgz", - "integrity": "sha512-FzNahnidyEPBCI0HcufJoSEoKykesRlFcSzQqjH9x0+LC8tnnE/p/90PBLu8iZTxr8yYZNyTtiAujUqyN+CIxw==", + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-2.2.4.tgz", + "integrity": "sha512-QCHGyZEK0bfi9GR215QSm+NJwFKEShbtc7tfbUdLAEzn3kKhLDDZqvljn8rPZM9v8CEOhzL1nlYoO4r1ryl67w==", "dev": true, "requires": { - "handlebars": "^4.1.0" + "handlebars": "^4.1.2" } }, "jest": { @@ -13921,9 +13826,9 @@ } }, "jquery": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.2.1.tgz", - "integrity": "sha1-XE2d5lKvbNCncBVKYxu6ErAVx4c=", + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.4.0.tgz", + "integrity": "sha512-ggRCXln9zEqv6OqAGXFEcshF5dSBvCkzj6Gm2gzuR5fWawaX8t7cxKVkkygKODrDAzKdoYw3l/e3pm3vlT4IbQ==", "dev": true }, "jquery-mousewheel": { @@ -13984,9 +13889,9 @@ "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" }, "js-yaml": { - "version": "3.13.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.0.tgz", - "integrity": "sha512-pZZoSxcCYco+DIKBTimr67J6Hy+EYGZDY/HCWC+iAEA9h1ByhMXAIVUXMcMFpOCxQ/xjXmPI2MkDL5HRm5eFrQ==", + "version": "3.13.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", + "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", "dev": true, "requires": { "argparse": "^1.0.7", @@ -20226,9 +20131,9 @@ "dev": true }, "uglify-js": { - "version": "3.5.4", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.5.4.tgz", - "integrity": "sha512-GpKo28q/7Bm5BcX9vOu4S46FwisbPbAmkkqPnGIpKvKTM96I85N6XHQV+k4I6FA2wxgLhcsSyHoNhzucwCflvA==", + "version": "3.5.8", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.5.8.tgz", + "integrity": "sha512-GFSjB1nZIzoIq70qvDRtWRORHX3vFkAnyK/rDExc0BN7r9+/S+Voz3t/fwJuVfjppAMz+ceR2poE7tkhvnVwQQ==", "dev": true, "optional": true, "requires": { diff --git a/package.json b/package.json index 1e1b842710..895c9f81bd 100644 --- a/package.json +++ b/package.json @@ -76,7 +76,7 @@ "i18next-scanner": "1.9.4", "immutable": "^3.8.2", "jest": "^24.7.1", - "jquery": "3.2.1", + "jquery": "^3.4.0", "jquery-ui": "1.12.1", "jquery-ui-css": "1.11.4", "js-interpreter": "^1.4.6", diff --git a/src/botPage/bot/TradeEngine/OpenContract.js b/src/botPage/bot/TradeEngine/OpenContract.js index 6f6a36fe8f..94effe6f64 100644 --- a/src/botPage/bot/TradeEngine/OpenContract.js +++ b/src/botPage/bot/TradeEngine/OpenContract.js @@ -1,6 +1,6 @@ import { roundBalance } from '../../common/tools'; import { doUntilDone } from '../tools'; -import { contractStatus, contract as broadcastContract } from '../broadcast'; +import { contractStatus, contractSettled, contract as broadcastContract } from '../broadcast'; import { sell, openContractReceived } from './state/actions'; const AFTER_FINISH_TIMEOUT = 5; @@ -27,7 +27,9 @@ export default Engine => contractStatus({ id : 'contract.sold', data: contract.transaction_ids.sell, + contract, }); + contractSettled(contract); this.contractId = ''; this.updateTotals(contract); if (this.afterPromise) { @@ -67,7 +69,9 @@ export default Engine => this.unsubscribeOpenContract(); doUntilDone(() => this.api.subscribeToOpenContract(contractId)).then(r => { - ({ proposal_open_contract: { id: this.openContractId } } = r); + ({ + proposal_open_contract: { id: this.openContractId }, + } = r); }); } resetSubscriptionTimeout(timeout = this.getContractDuration() + AFTER_FINISH_TIMEOUT) { diff --git a/src/botPage/bot/broadcast.js b/src/botPage/bot/broadcast.js index 7ffc8f3d03..4c9a236ac6 100644 --- a/src/botPage/bot/broadcast.js +++ b/src/botPage/bot/broadcast.js @@ -4,6 +4,8 @@ export const contract = c => globalObserver.emit('bot.contract', c); export const contractStatus = c => globalObserver.emit('contract.status', c); +export const contractSettled = c => globalObserver.emit('contract.settled', c); + export const info = i => globalObserver.emit('bot.info', i); export const notify = (className, message) => globalObserver.emit('Notify', { className, message, position: 'right' }); diff --git a/src/botPage/view/Dialogs/Limits.js b/src/botPage/view/Dialogs/Limits.js index 33332b2b59..e0b87119c5 100644 --- a/src/botPage/view/Dialogs/Limits.js +++ b/src/botPage/view/Dialogs/Limits.js @@ -1,59 +1,122 @@ +import { LiveApi } from 'binary-live-api'; import React, { PureComponent } from 'react'; import PropTypes from 'prop-types'; -import { translate } from '../../../common/i18n'; -import * as style from '../style'; import Dialog from './Dialog'; import { restrictInputCharacter } from '../shared'; +import * as style from '../style'; +import { getToken } from '../../../common/utils/storageManager'; +import { showSpinnerInButton, removeSpinnerInButton, createUrl, translate } from '../../../common/utils/tools'; class LimitsContent extends PureComponent { constructor() { super(); this.state = { error : '', - maxLoss : '', - maxTrades: '', + maxTrades: 0, + maxLosses: null, }; } + submit() { - const maxLoss = parseFloat(this.state.maxLoss || 0); - const maxTrades = parseInt(this.state.maxTrades || 0); this.setState({ error: '' }); - if (maxTrades <= 0 || maxTrades > 100) { - this.setState({ error: 'Maximum number of trades should be between 1 and 100.' }); + + const onSave = () => { + this.props.onSave({ + maxTrades: this.state.maxTrades, + maxLoss : this.state.maxLosses, + }); + }; + + if (!this.state.maxLosses) { + this.updateMaxLosses() + .then(() => { + onSave(); + }) + .catch(() => { + this.setState({ + error: translate( + 'Please set your daily loss limit in the Self-Exclusion Facilities page to allow trading.' + ), + }); + }); return; } - if (!maxLoss) { - this.setState({ error: 'Please enter a Maximum Loss amount greater than zero.' }); + + if (this.state.maxTrades <= 0 || this.state.maxTrades > 100) { + this.setState({ error: translate('Maximum consecutive trades should be between 1 and 100') }); return; } - this.props.onSave({ - maxLoss, - maxTrades, + + onSave(); + } + + updateMaxLosses() { + return new Promise((resolve, reject) => { + const { api } = this.props; + const $startButton = $('#submit-trade-limits'); + const initialText = $startButton.text(); + + showSpinnerInButton($startButton); + + api.getSelfExclusion() + .then(response => { + const { max_losses: maxLosses } = response.get_self_exclusion; + let callback; + + if (maxLosses) { + this.setState({ maxLosses }); + callback = resolve; + } else { + callback = reject; + } + + removeSpinnerInButton($startButton, initialText); + callback(); + }) + .catch(() => { + removeSpinnerInButton($startButton, initialText); + reject(); + }); }); } + componentDidMount() { const cleanupLayout = () => { this.setState({ - maxTrades: '', - maxLoss : '', + maxTrades: 0, error : '', }); }; + + const onDialogOpen = () => { + this.updateMaxLosses().catch(() => {}); + }; + $('#limits-dialog-component').dialog({ + open : onDialogOpen, close : cleanupLayout, autoOpen: false, }); } + onMaxTradeChange(e) { if (restrictInputCharacter({ input: e.target.value, whitelistRegEx: '^[\\d]*$' })) { this.setState({ maxTrades: e.target.value }); } } - onMaxLossChange(e) { - if (restrictInputCharacter({ input: e.target.value, whitelistRegEx: '^\\d*\\.?\\d*$' })) { - this.setState({ maxLoss: e.target.value }); + + getDailyLossesLimit() { + if (this.state.maxLosses) { + const token = $('.account-id') + .first() + .attr('value'); + const tokenObj = getToken(token); + const currency = tokenObj && tokenObj.loginInfo.currency; + return currency ? `${this.state.maxLosses} ${currency}` : `${this.state.maxLosses}`; } + return translate('Not set'); } + render() { return (
-
-
- -
-
- -
- {this.state.error ?

{this.state.error}

: null} -

{translate( - 'Trade limitations are required by our regulators. Your bot will conclude trading when one or both of the conditions are met.' + 'We require you to set trade limitations in compliance with business regulations. Please note that your bot will only stop trading if any or both of the conditions below are met.' )}

-
- +
+ +
+ {translate([ + 'This is the threshold that limits your potential losses for the day in all Binary.com platforms. Once your total loss reaches or exceeds this amount, your bot will stop trading. Please set a value in the [_1]Self-Exclusion Facilities page[_2].', + ``, + '', + ])} +
+
+
+ +
+ {translate( + 'This is the maximum number of trades that you allow your bot to execute for this run.' + )} +
+
+
+ { + this.maxTradesDiv = el; + }} + type="text" + id="limitation-max-trades" + step="1" + maxLength="3" + value={this.state.maxTrades} + onChange={(...args) => this.onMaxTradeChange(...args)} + data-lpignore={true} + /> +
+ {this.state.error &&

{this.state.error}

} +
+
); } static props = { onSave: PropTypes.func, + api : PropTypes.instanceOf(LiveApi), }; } export default class Limits extends Dialog { - constructor() { + constructor(api) { const onSave = limits => { this.limitsPromise(limits); this.close(); }; - super('limits-dialog', translate('Trade Limitations'), , style.dialogLayout); + super( + 'limits-dialog', + translate('Trade Limitations'), + , + style.dialogLayout + ); + this.registerCloseOnOtherDialog(); } getLimits() { this.open(); diff --git a/src/botPage/view/TradeInfoPanel/TradeTable.js b/src/botPage/view/TradeInfoPanel/TradeTable.js index fe5bdef7b1..adb7881c28 100644 --- a/src/botPage/view/TradeInfoPanel/TradeTable.js +++ b/src/botPage/view/TradeInfoPanel/TradeTable.js @@ -19,10 +19,18 @@ const getProfit = ({ sell_price: sellPrice, buy_price: buyPrice, currency }) => return ''; }; +const getTimestamp = date => { + const buyDate = new Date(date * 1000); + return `${buyDate.toISOString().split('T')[0]} ${buyDate.toTimeString().slice(0, 8)} ${ + buyDate.toTimeString().split(' ')[1] + }`; +}; + const minHeight = 290; const rowHeight = 25; -const ProfitColor = ({ value }) =>
0 ? style.green : style.red}>{value}
; +const ProfitColor = ({ value }) =>
0 ? style.greenLeft : style.redLeft}>{value}
; +const StatusFormat = ({ value }) =>
{value}
; export default class TradeTable extends Component { constructor({ accountID }) { @@ -39,15 +47,18 @@ export default class TradeTable extends Component { }; this.columns = [ { key: 'timestamp', width: 192, resizable: true, name: translate('Timestamp') }, - { key: 'reference', width: 142, resizable: true, name: translate('Reference') }, - { key: 'contract_type', width: 104, resizable: true, name: translate('Trade type') }, - { key: 'entry_tick', width: 80, resizable: true, name: translate('Entry spot') }, - { key: 'exit_tick', width: 70, resizable: true, name: translate('Exit spot') }, + { key: 'reference', width: 110, resizable: true, name: translate('Reference') }, + { key: 'contract_type', width: 70, resizable: true, name: translate('Trade type') }, + { key: 'entry_tick', width: 75, resizable: true, name: translate('Entry spot') }, + { key: 'exit_tick', width: 75, resizable: true, name: translate('Exit spot') }, { key: 'buy_price', width: 80, resizable: true, name: translate('Buy price') }, { key: 'profit', width: 80, resizable: true, name: translate('Profit/Loss'), formatter: ProfitColor }, + { key: 'contract_status', width: 70, resizable: true, name: translate('Status'), formatter: StatusFormat }, ]; } componentWillMount() { + const { api } = this.props; + globalObserver.register('summary.export', () => { const accountData = this.state[this.props.accountID]; if (accountData && accountData.rows.length > 0) { @@ -68,16 +79,14 @@ export default class TradeTable extends Component { if (!info) { return; } - const buyDate = new Date(info.date_start * 1000); - const timestamp = `${buyDate.toISOString().split('T')[0]} ${buyDate.toTimeString().slice(0, 8)} ${ - buyDate.toTimeString().split(' ')[1] - }`; + const timestamp = getTimestamp(info.date_start); const tradeObj = { reference: info.transaction_ids.buy, ...info, timestamp }; const { accountID } = tradeObj; const trade = { ...tradeObj, - profit: getProfit(tradeObj), + profit : getProfit(tradeObj), + contract_status: translate('Pending'), }; const accountStat = this.getAccountStat(accountID); @@ -93,6 +102,45 @@ export default class TradeTable extends Component { this.setState({ [accountID]: appendRow(trade, accountStat) }); } }); + globalObserver.register('contract.settled', contract => { + this.registerTimeout(api, contract); + }); + } + registerTimeout = (api, contract) => { + setTimeout(() => { + const contractID = contract.contract_id; + this.refreshContract(api, contractID); + }, 3000); + }; + refreshContract(api, contractID) { + api.getContractInfo(contractID).then(r => { + const contract = r.proposal_open_contract; + const timestamp = getTimestamp(contract.date_start); + const tradeObj = { reference: contract.transaction_ids.buy, ...contract, timestamp }; + const { accountID } = this.props; + + const trade = { + ...tradeObj, + profit: getProfit(tradeObj), + }; + + if (trade.is_expired && trade.is_sold && !trade.exit_tick) trade.exit_tick = '-'; + + const { id } = this.state[accountID]; + const rows = this.state[accountID].rows.slice(); + const updatedRows = rows.map(row => { + const { reference } = row; + if (reference === trade.reference) { + return { + contract_status: translate('Settled'), + reference, + ...trade, + }; + } + return row; + }); + this.setState({ [accountID]: { id, rows: updatedRows } }); + }); } rowGetter(i) { const { accountID } = this.props; diff --git a/src/botPage/view/TradeInfoPanel/index.js b/src/botPage/view/TradeInfoPanel/index.js index 20c70827a9..125828dc4c 100644 --- a/src/botPage/view/TradeInfoPanel/index.js +++ b/src/botPage/view/TradeInfoPanel/index.js @@ -171,7 +171,7 @@ export default class TradeInfoPanel extends Component {
- +
diff --git a/src/botPage/view/View.js b/src/botPage/view/View.js index cb626fbd12..d54b3df8ce 100644 --- a/src/botPage/view/View.js +++ b/src/botPage/view/View.js @@ -142,7 +142,6 @@ const clearRealityCheck = () => { stopRealityCheck(); }; -const limits = new Limits(); const integrationsDialog = new IntegrationsDialog(); const loadDialog = new LoadDialog(); const saveDialog = new SaveDialog(); @@ -518,8 +517,13 @@ export default class View { .attr('value'); const tokenObj = getToken(token); initRealityCheck(() => $('#stopButton').triggerHandler('click')); + if (tokenObj && tokenObj.hasTradeLimitation) { - limits.getLimits().then(startBot); + const limits = new Limits(api); + limits + .getLimits() + .then(startBot) + .catch(() => {}); } else { startBot(); } @@ -684,6 +688,6 @@ function renderReactComponents() { />, $('#footer')[0] ); - ReactDOM.render(, $('#summaryPanel')[0]); + ReactDOM.render(, $('#summaryPanel')[0]); ReactDOM.render(, $('#logTable')[0]); } diff --git a/src/botPage/view/style.js b/src/botPage/view/style.js index 9061ff5507..ca2935034c 100644 --- a/src/botPage/view/style.js +++ b/src/botPage/view/style.js @@ -75,10 +75,16 @@ export const bottomWarningLink = { textDecoration: 'underline' }; export const green = { color: 'green' }; +export const greenLeft = { color: 'green', float: 'left' }; + export const red = { color: 'red' }; +export const redLeft = { color: 'red', float: 'left' }; + export const tradePanelAccount = { float: 'right' }; +export const left = { float: 'left' }; + export const warningText = { 'font-size': '12px', color : 'lightgray', diff --git a/static/css/_panel.scss b/static/css/_panel.scss index 27cc5c61ca..e4b7dcd604 100644 --- a/static/css/_panel.scss +++ b/static/css/_panel.scss @@ -53,7 +53,7 @@ $disabled-color: #F2F2F2; margin-bottom: 1em; } p { - margin: 0em; + margin: 0em; } #sync-warning { font-size: 11px; @@ -320,6 +320,24 @@ $disabled-color: #F2F2F2; .ui-dialog-content { padding: 0px !important; + + &.ui-widget-content { + input[type=text] { + height: 40px; + width: 100%; + } + .description { + font-size: 75%; + margin: 0.5em 0; + } + .input-row { + margin: 1em 0; + + &.last { + margin-bottom: 0; + } + } + } } #load-dialog, #save-dialog { @@ -328,23 +346,7 @@ $disabled-color: #F2F2F2; } } -#load-dialog, #save-dialog, #integrations-dialog { - .input-row { - margin: 1em 0; - } - .input-row.last { - margin-bottom: 0; - } - & .description { - font-size: 75%; - } -} - #save-dialog { - #save-filename { - width: 100%; - height: 40px; - } #collection { padding: 1.2em; border: 1px solid $brand-gray; @@ -367,9 +369,6 @@ $disabled-color: #F2F2F2; font-size: 20px; margin: 0 0 0.2em 0; } - .description { - margin: 0 0 0.2em 0; - } .left { width: 60%; display: table-cell;