From b153e6ce353e0e600954eb7f8676a511531fdce4 Mon Sep 17 00:00:00 2001 From: Brandon Mino Date: Thu, 4 Apr 2019 00:04:06 -0400 Subject: [PATCH 1/3] Move Execution Check into Execution --- src/main/ArbitrageExecution.js | 2 ++ src/main/Main.js | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/ArbitrageExecution.js b/src/main/ArbitrageExecution.js index 463f03f..ac23ae1 100644 --- a/src/main/ArbitrageExecution.js +++ b/src/main/ArbitrageExecution.js @@ -10,6 +10,8 @@ const ArbitrageExecution = { balances: {}, executeCalculatedPosition(calculated) { + if (!ArbitrageExecution.isSafeToExecute(calculated)) return false; + // Register trade id as being executed ArbitrageExecution.inProgressIds.add(calculated.id); ArbitrageExecution.orderHistory[calculated.id] = new Date().getTime(); diff --git a/src/main/Main.js b/src/main/Main.js index b1e2444..f55ced1 100644 --- a/src/main/Main.js +++ b/src/main/Main.js @@ -56,10 +56,10 @@ function calculateArbitrage() { MarketCache.relationships.forEach(relationship => { try { - let calculated = CalculationNode.optimize(relationship); + const calculated = CalculationNode.optimize(relationship); if (calculated) { if (CONFIG.HUD.ENABLED) results[calculated.id] = calculated; - if (ArbitrageExecution.isSafeToExecute(calculated)) ArbitrageExecution.executeCalculatedPosition(calculated); + ArbitrageExecution.executeCalculatedPosition(calculated); } } catch (error) { logger.performance.debug(error.message); From 90d8d925bdf2bbc7460c4164da4a12fb6d875e82 Mon Sep 17 00:00:00 2001 From: Brandon Mino Date: Thu, 4 Apr 2019 00:08:11 -0400 Subject: [PATCH 2/3] Execution Checks Check for currently engaged symbols Record order execution time when execution completes --- src/main/ArbitrageExecution.js | 61 +++++++++++++++++++++++++--------- 1 file changed, 45 insertions(+), 16 deletions(-) diff --git a/src/main/ArbitrageExecution.js b/src/main/ArbitrageExecution.js index ac23ae1..ab0d104 100644 --- a/src/main/ArbitrageExecution.js +++ b/src/main/ArbitrageExecution.js @@ -6,6 +6,7 @@ const _ = require ('lodash'); const ArbitrageExecution = { inProgressIds: new Set(), + inProgressSymbols: new Set(), orderHistory: {}, balances: {}, @@ -14,7 +15,9 @@ const ArbitrageExecution = { // Register trade id as being executed ArbitrageExecution.inProgressIds.add(calculated.id); - ArbitrageExecution.orderHistory[calculated.id] = new Date().getTime(); + ArbitrageExecution.inProgressSymbols.add(calculated.trade.symbol.a); + ArbitrageExecution.inProgressSymbols.add(calculated.trade.symbol.b); + ArbitrageExecution.inProgressSymbols.add(calculated.trade.symbol.c); const before = new Date().getTime(); const initialBalances = _.cloneDeep(ArbitrageExecution.balances); @@ -36,30 +39,54 @@ const ArbitrageExecution = { }) .then(() => { ArbitrageExecution.inProgressIds.delete(calculated.id); + ArbitrageExecution.inProgressSymbols.delete(calculated.trade.symbol.a); + ArbitrageExecution.inProgressSymbols.delete(calculated.trade.symbol.b); + ArbitrageExecution.inProgressSymbols.delete(calculated.trade.symbol.c); }); }, isSafeToExecute(calculated) { + const SECONDS_IN_ONE_DAY = 60 * 60 * 24; + + // Profit Threshold is Not Satisfied if (calculated.percent < CONFIG.TRADING.PROFIT_THRESHOLD) return false; + // Age Threshold is Not Satisfied const ageInMilliseconds = new Date().getTime() - Math.min(calculated.times.ab, calculated.times.bc, calculated.times.ca); if (ageInMilliseconds > CONFIG.TRADING.AGE_THRESHOLD) return false; - if (CONFIG.TRADING.EXECUTION_CAP && Object.keys(ArbitrageExecution.orderHistory).length >= CONFIG.TRADING.EXECUTION_CAP && ArbitrageExecution.inProgressIds.size === 0) { - const msg = `Cannot exceed execution cap of ${CONFIG.TRADING.EXECUTION_CAP} execution`; + if (CONFIG.TRADING.EXECUTION_CAP && ArbitrageExecution.inProgressIds.size === 0 && ArbitrageExecution.getExecutionAttemptCount() >= CONFIG.TRADING.EXECUTION_CAP) { + const msg = `Cannot exceed user defined execution cap of ${CONFIG.TRADING.EXECUTION_CAP} executions`; logger.execution.error(msg); process.exit(); + return false; } - if (CONFIG.TRADING.EXECUTION_CAP && Object.keys(ArbitrageExecution.orderHistory).length >= CONFIG.TRADING.EXECUTION_CAP) { + if (CONFIG.TRADING.EXECUTION_CAP && ArbitrageExecution.getExecutionAttemptCount() >= CONFIG.TRADING.EXECUTION_CAP) { logger.execution.trace(`Blocking execution because ${Object.keys(ArbitrageExecution.orderHistory).length}/${CONFIG.TRADING.EXECUTION_CAP} executions have been attempted`); return false; } if (ArbitrageExecution.inProgressIds.has(calculated.id)) { - logger.execution.trace(`Blocking execution because ${calculated.id} is already being executed`); + logger.execution.trace(`Blocking execution because ${calculated.id} is currently being executed`); + return false; + } + if (ArbitrageExecution.inProgressSymbols.has(calculated.trade.symbol.a)) { + logger.execution.trace(`Blocking execution because ${calculated.trade.symbol.a} is currently involved in an execution`); + return false; + } + if (ArbitrageExecution.inProgressSymbols.has(calculated.trade.symbol.b)) { + logger.execution.trace(`Blocking execution because ${calculated.trade.symbol.b} is currently involved in an execution`); + return false; + } + if (ArbitrageExecution.inProgressSymbols.has(calculated.trade.symbol.c)) { + logger.execution.trace(`Blocking execution because ${calculated.trade.symbol.c} is currently involved in an execution`); return false; } - if (ArbitrageExecution.tradesInXSeconds(10) >= 3) { - logger.execution.trace(`Blocking execution because ${ArbitrageExecution.tradesInXSeconds(10)} trades have already been executed in the last 10 seconds`); + if (ArbitrageExecution.executedTradesInLastXSeconds(SECONDS_IN_ONE_DAY) > 10000) { + logger.execution.trace(`Blocking execution because ${ArbitrageExecution.executedTradesInLastXSeconds(SECONDS_IN_ONE_DAY)} trades have been completed in the past 24 hours`); + return false; + } + if (ArbitrageExecution.executedTradesInLastXSeconds(10) >= 7) { + logger.execution.trace(`Blocking execution because ${ArbitrageExecution.executedTradesInLastXSeconds(10)} trades have been completed in the last 10 seconds`); return false; } @@ -83,13 +110,19 @@ const ArbitrageExecution = { return differences; }, - tradesInXSeconds(seconds) { + executedTradesInLastXSeconds(seconds) { const timeFloor = new Date().getTime() - (seconds * 1000); - return Object.values(ArbitrageExecution.orderHistory).filter(time => time > timeFloor).length; + return Object.values(ArbitrageExecution.orderHistory).filter(time => time > timeFloor).length * 3; + }, + + getExecutionAttemptCount() { + return Object.keys(ArbitrageExecution.orderHistory).length; }, execute(calculated) { - return ArbitrageExecution.getExecutionStrategy()(calculated); + ArbitrageExecution.orderHistory[calculated.id] = new Date().getTime(); + return ArbitrageExecution.getExecutionStrategy()(calculated) + .then(() => ArbitrageExecution.orderHistory[calculated.id] = new Date().getTime()); }, getExecutionStrategy() { @@ -111,12 +144,8 @@ const ArbitrageExecution = { linearExecutionStrategy(calculated) { return BinanceApi.marketBuyOrSell(calculated.trade.ab.method)(calculated.trade.ab.ticker, calculated.ab.market) - .then(() => { - return BinanceApi.marketBuyOrSell(calculated.trade.bc.method)(calculated.trade.bc.ticker, calculated.bc.market); - }) - .then(() => { - return BinanceApi.marketBuyOrSell(calculated.trade.ca.method)(calculated.trade.ca.ticker, calculated.ca.market); - }); + .then(() => BinanceApi.marketBuyOrSell(calculated.trade.bc.method)(calculated.trade.bc.ticker, calculated.bc.market)) + .then(() => BinanceApi.marketBuyOrSell(calculated.trade.ca.method)(calculated.trade.ca.ticker, calculated.ca.market)); } }; From 2598ea34490b9e7efebdcedd7bd70219d0a46bd5 Mon Sep 17 00:00:00 2001 From: Brandon Mino Date: Thu, 4 Apr 2019 00:31:32 -0400 Subject: [PATCH 3/3] Update Version --- package-lock.json | 50 ++++++++++++----------------------------------- package.json | 8 +++----- 2 files changed, 16 insertions(+), 42 deletions(-) diff --git a/package-lock.json b/package-lock.json index 5700f65..67d3703 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "binance-triangle-arbitrage", - "version": "4.4.4", + "version": "4.4.5", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -27,7 +27,6 @@ "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" } @@ -36,7 +35,6 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/args/-/args-5.0.1.tgz", "integrity": "sha512-1kqmFCFsPffavQFGt8OxJdIcETti99kySRUPMpOhaGjL6mRJn8HFU1OxKY5bMqfZKUwTQc1mZkAjmGYaVOHFtQ==", - "dev": true, "requires": { "camelcase": "5.0.0", "chalk": "2.4.2", @@ -101,8 +99,7 @@ "camelcase": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.0.0.tgz", - "integrity": "sha512-faqwZqnWxbxn+F1d399ygeamQNy3lPp/H9H6rNrqYh4FSVCtcY+3cub1MxA8o9mDd55mM8Aghuu/kuyYA6VTsA==", - "dev": true + "integrity": "sha512-faqwZqnWxbxn+F1d399ygeamQNy3lPp/H9H6rNrqYh4FSVCtcY+3cub1MxA8o9mDd55mM8Aghuu/kuyYA6VTsA==" }, "caseless": { "version": "0.12.0", @@ -113,7 +110,6 @@ "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", @@ -124,7 +120,6 @@ "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, "requires": { "color-name": "1.1.3" } @@ -132,8 +127,7 @@ "color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", - "dev": true + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" }, "combined-stream": { "version": "1.0.7", @@ -159,8 +153,7 @@ "dateformat": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-3.0.3.tgz", - "integrity": "sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q==", - "dev": true + "integrity": "sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q==" }, "debug": { "version": "3.2.6", @@ -188,7 +181,6 @@ "version": "1.4.1", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==", - "dev": true, "requires": { "once": "^1.4.0" } @@ -209,8 +201,7 @@ "escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "dev": true + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" }, "extend": { "version": "3.0.2", @@ -230,8 +221,7 @@ "fast-json-parse": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/fast-json-parse/-/fast-json-parse-1.0.3.tgz", - "integrity": "sha512-FRWsaZRWEJ1ESVNbDWmsAlqDk96gPQezzLghafp5J4GUKjbCz3OkAHuZs5TuPEtkbVQERysLp9xv6c24fBm8Aw==", - "dev": true + "integrity": "sha512-FRWsaZRWEJ1ESVNbDWmsAlqDk96gPQezzLghafp5J4GUKjbCz3OkAHuZs5TuPEtkbVQERysLp9xv6c24fBm8Aw==" }, "fast-json-stable-stringify": { "version": "2.0.0", @@ -293,8 +283,7 @@ "has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" }, "http-signature": { "version": "1.2.0", @@ -318,8 +307,7 @@ "inherits": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", - "dev": true + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" }, "ip": { "version": "1.1.5", @@ -339,8 +327,7 @@ "jmespath": { "version": "0.15.0", "resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.15.0.tgz", - "integrity": "sha1-o/Iiqarp+Wb10nx5ZRDigJF2Qhc=", - "dev": true + "integrity": "sha1-o/Iiqarp+Wb10nx5ZRDigJF2Qhc=" }, "jsbn": { "version": "0.1.1", @@ -376,8 +363,7 @@ "leven": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/leven/-/leven-2.1.0.tgz", - "integrity": "sha1-wuep93IJTe6dNCAq6KzORoeHVYA=", - "dev": true + "integrity": "sha1-wuep93IJTe6dNCAq6KzORoeHVYA=" }, "lodash": { "version": "4.17.11", @@ -400,8 +386,7 @@ "mri": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/mri/-/mri-1.1.4.tgz", - "integrity": "sha512-6y7IjGPm8AzlvoUrwAaw1tLnUBudaS3752vcd8JtrpGGQn+rXIe63LFVHm/YMwtqAuh+LJPCFdlLYPWM1nYn6w==", - "dev": true + "integrity": "sha512-6y7IjGPm8AzlvoUrwAaw1tLnUBudaS3752vcd8JtrpGGQn+rXIe63LFVHm/YMwtqAuh+LJPCFdlLYPWM1nYn6w==" }, "ms": { "version": "2.1.1", @@ -431,7 +416,6 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true, "requires": { "wrappy": "1" } @@ -458,7 +442,6 @@ "version": "2.5.0", "resolved": "https://registry.npmjs.org/pino-pretty/-/pino-pretty-2.5.0.tgz", "integrity": "sha512-odR4SKdyubhe4aFts0/mBau2/mJLG23Ghyo86a+GZ2/Cev3CRr5nYv2+82V7v1hQL93yRSO004ASrrF7278TNQ==", - "dev": true, "requires": { "args": "^5.0.0", "chalk": "^2.3.2", @@ -485,7 +468,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "dev": true, "requires": { "end-of-stream": "^1.1.0", "once": "^1.3.1" @@ -515,7 +497,6 @@ "version": "3.2.0", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.2.0.tgz", "integrity": "sha512-RV20kLjdmpZuTF1INEb9IA3L68Nmi+Ri7ppZqo78wj//Pn62fCoJyV9zalccNzDD/OuJpMG4f+pfMl8+L6QdGw==", - "dev": true, "requires": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", @@ -594,7 +575,6 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/split2/-/split2-3.1.1.tgz", "integrity": "sha512-emNzr1s7ruq4N+1993yht631/JH+jaj0NYBosuKmLcq+JkGQ9MmTw1RB1fGaTCzUuseRIClrlSLHRNYGwWQ58Q==", - "dev": true, "requires": { "readable-stream": "^3.0.0" } @@ -624,7 +604,6 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.2.0.tgz", "integrity": "sha512-6YqyX6ZWEYguAxgZzHGL7SsCeGx3V2TtOTqZz1xSTSWnqsbWwbptafNyvf/ACquZUXV3DANr5BDIwNYe1mN42w==", - "dev": true, "requires": { "safe-buffer": "~5.1.0" } @@ -633,7 +612,6 @@ "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" } @@ -694,8 +672,7 @@ "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", - "dev": true + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" }, "uuid": { "version": "3.3.2", @@ -715,8 +692,7 @@ "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" }, "ws": { "version": "4.1.0", diff --git a/package.json b/package.json index 503c551..0aed444 100644 --- a/package.json +++ b/package.json @@ -1,9 +1,9 @@ { "name": "binance-triangle-arbitrage", - "version": "4.4.4", + "version": "4.4.5", "private": true, "engines": { - "node": "11.10.0", + "node": "11.10.1", "npm": "6.9.0" }, "scripts": { @@ -13,9 +13,7 @@ "blessed": "^0.1.81", "lodash": "^4.17.11", "node-binance-api": "^0.9.0", - "pino": "^5.11.1" - }, - "devDependencies": { + "pino": "^5.11.1", "pino-pretty": "^2.5.0" } }