From 978fd14ec615e57da86713a3e366fc7a179a6f3b Mon Sep 17 00:00:00 2001 From: Mike van Rossum Date: Thu, 9 Aug 2018 00:12:35 +0800 Subject: [PATCH 01/29] [GB] introduce optimizedConnection flag in binance --- exchange/orders/sticky.js | 1 + exchange/wrappers/binance.js | 18 ++++++++++++++++-- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/exchange/orders/sticky.js b/exchange/orders/sticky.js index 138aa82f5..385f27376 100644 --- a/exchange/orders/sticky.js +++ b/exchange/orders/sticky.js @@ -203,6 +203,7 @@ class StickyOrder extends BaseOrder { throw err; this.ticker = ticker; + this.emit('ticker', ticker); const bookSide = this.side === 'buy' ? 'bid' : 'ask'; // note: might be string VS float diff --git a/exchange/wrappers/binance.js b/exchange/wrappers/binance.js index d3e674b30..07b69e2c7 100644 --- a/exchange/wrappers/binance.js +++ b/exchange/wrappers/binance.js @@ -10,7 +10,9 @@ const Binance = require('binance'); const Trader = function(config) { _.bindAll(this, [ 'roundAmount', - 'roundPrice' + 'roundPrice', + 'isValidPrice', + 'isValidLot' ]); if (_.isObject(config)) { @@ -20,6 +22,18 @@ const Trader = function(config) { this.asset = config.asset.toUpperCase(); } + let recvWindow = 6000; + if(config.optimizedConnection) { + // there is a bug in binance's API + // where some requests randomly take + // over a second, this tells binance + // to bail out after 500ms. + // + // As discussed in binance API + // telegram. TODO add link. + recvWindow = 500; + } + this.pair = this.asset + this.currency; this.name = 'binance'; @@ -31,7 +45,7 @@ const Trader = function(config) { key: this.key, secret: this.secret, timeout: 15000, - recvWindow: 60000, // suggested by binance + recvWindow, disableBeautification: false, handleDrift: true, }); From 3246dcfd340fd7e2e627cf75dac4f5258a9b0307 Mon Sep 17 00:00:00 2001 From: Mike van Rossum Date: Thu, 9 Aug 2018 15:41:54 +0800 Subject: [PATCH 02/29] [GB] catch strange networking error --- exchange/wrappers/binance.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/exchange/wrappers/binance.js b/exchange/wrappers/binance.js index 07b69e2c7..d5d1520a3 100644 --- a/exchange/wrappers/binance.js +++ b/exchange/wrappers/binance.js @@ -77,7 +77,8 @@ const recoverableErrors = [ 'Response code 429', 'Response code 5', 'Response code 403', - 'ETIMEDOUT' + 'ETIMEDOUT', + 'EHOSTUNREACH' ]; const includes = (str, list) => { @@ -298,6 +299,7 @@ Trader.prototype.isValidPrice = function(price) { } Trader.prototype.isValidLot = function(price, amount) { + console.log('isValidLot', this.market.minimalOrder.order, amount * price >= this.market.minimalOrder.order) return amount * price >= this.market.minimalOrder.order; } From 98e69817bc81844fdc0d6b0dd09ece482f51303a Mon Sep 17 00:00:00 2001 From: Mike van Rossum Date: Thu, 9 Aug 2018 15:50:57 +0800 Subject: [PATCH 03/29] update binance market files, see #2406 --- exchange/wrappers/binance-markets.json | 236 ++++++++++++++++++------- 1 file changed, 175 insertions(+), 61 deletions(-) diff --git a/exchange/wrappers/binance-markets.json b/exchange/wrappers/binance-markets.json index 4520cd2ef..c26a20cc0 100644 --- a/exchange/wrappers/binance-markets.json +++ b/exchange/wrappers/binance-markets.json @@ -1,7 +1,6 @@ { "assets": [ "NULS", - "VEN", "BNB", "NEO", "LINK", @@ -39,6 +38,7 @@ "AION", "AMB", "APPC", + "ARDR", "ARK", "ARN", "BAT", @@ -60,6 +60,7 @@ "DENT", "DGD", "DLT", + "DOCK", "EDO", "ELF", "ENG", @@ -71,6 +72,7 @@ "GTO", "GVT", "GXS", + "HOT", "HSR", "ICX", "INS", @@ -101,6 +103,7 @@ "PIVX", "POA", "POE", + "POLY", "POWR", "PPT", "QKC", @@ -125,6 +128,7 @@ "TRIG", "TRX", "TUSD", + "VET", "VIA", "VIB", "VIBE", @@ -156,22 +160,11 @@ "NULS" ], "minimalOrder": { - "amount": 0.01, + "amount": 0.1, "price": 0.00001, "order": 1 } }, - { - "pair": [ - "BNB", - "VEN" - ], - "minimalOrder": { - "amount": 0.01, - "price": 0.0001, - "order": 1 - } - }, { "pair": [ "BTC", @@ -684,7 +677,7 @@ "ADA" ], "minimalOrder": { - "amount": 0.01, + "amount": 0.1, "price": 0.00001, "order": 1 } @@ -728,7 +721,7 @@ "ADX" ], "minimalOrder": { - "amount": 0.01, + "amount": 0.1, "price": 0.00001, "order": 1 } @@ -761,7 +754,7 @@ "AE" ], "minimalOrder": { - "amount": 0.01, + "amount": 0.1, "price": 0.00001, "order": 1 } @@ -794,7 +787,7 @@ "AGI" ], "minimalOrder": { - "amount": 0.01, + "amount": 0.1, "price": 0.00001, "order": 1 } @@ -827,7 +820,7 @@ "AION" ], "minimalOrder": { - "amount": 0.01, + "amount": 0.1, "price": 0.00001, "order": 1 } @@ -860,7 +853,7 @@ "AMB" ], "minimalOrder": { - "amount": 0.01, + "amount": 0.1, "price": 0.00001, "order": 1 } @@ -893,7 +886,7 @@ "APPC" ], "minimalOrder": { - "amount": 0.01, + "amount": 0.1, "price": 0.00001, "order": 1 } @@ -920,6 +913,39 @@ "order": 0.01 } }, + { + "pair": [ + "BNB", + "ARDR" + ], + "minimalOrder": { + "amount": 0.1, + "price": 0.00001, + "order": 1 + } + }, + { + "pair": [ + "BTC", + "ARDR" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "ARDR" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.01 + } + }, { "pair": [ "BTC", @@ -981,7 +1007,7 @@ "BAT" ], "minimalOrder": { - "amount": 0.01, + "amount": 0.1, "price": 0.00001, "order": 1 } @@ -1102,7 +1128,7 @@ "BCPT" ], "minimalOrder": { - "amount": 0.01, + "amount": 0.1, "price": 0.00001, "order": 1 } @@ -1135,7 +1161,7 @@ "BLZ" ], "minimalOrder": { - "amount": 0.01, + "amount": 0.1, "price": 0.00001, "order": 1 } @@ -1190,7 +1216,7 @@ "BRD" ], "minimalOrder": { - "amount": 0.01, + "amount": 0.1, "price": 0.00001, "order": 1 } @@ -1245,7 +1271,7 @@ "BTS" ], "minimalOrder": { - "amount": 0.01, + "amount": 0.1, "price": 0.00001, "order": 1 } @@ -1344,7 +1370,7 @@ "CMT" ], "minimalOrder": { - "amount": 0.01, + "amount": 0.1, "price": 0.00001, "order": 1 } @@ -1410,7 +1436,7 @@ "CVC" ], "minimalOrder": { - "amount": 0.01, + "amount": 0.1, "price": 0.00001, "order": 1 } @@ -1531,7 +1557,7 @@ "DLT" ], "minimalOrder": { - "amount": 0.01, + "amount": 0.1, "price": 0.00001, "order": 1 } @@ -1569,6 +1595,28 @@ "order": 0.001 } }, + { + "pair": [ + "BTC", + "DOCK" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "DOCK" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.01 + } + }, { "pair": [ "BTC", @@ -1795,7 +1843,7 @@ "GNT" ], "minimalOrder": { - "amount": 0.01, + "amount": 0.1, "price": 0.00001, "order": 1 } @@ -1850,7 +1898,7 @@ "GTO" ], "minimalOrder": { - "amount": 0.01, + "amount": 0.1, "price": 0.00001, "order": 1 } @@ -1921,6 +1969,28 @@ "order": 0.01 } }, + { + "pair": [ + "BTC", + "HOT" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "HOT" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.01 + } + }, { "pair": [ "BTC", @@ -1960,7 +2030,7 @@ "ICX" ], "minimalOrder": { - "amount": 0.01, + "amount": 0.1, "price": 0.00001, "order": 1 } @@ -2048,7 +2118,7 @@ "IOTA" ], "minimalOrder": { - "amount": 0.01, + "amount": 0.1, "price": 0.00001, "order": 1 } @@ -2158,7 +2228,7 @@ "LOOM" ], "minimalOrder": { - "amount": 0.01, + "amount": 0.1, "price": 0.00001, "order": 1 } @@ -2323,7 +2393,7 @@ "MCO" ], "minimalOrder": { - "amount": 0.01, + "amount": 0.1, "price": 0.00001, "order": 1 } @@ -2488,7 +2558,7 @@ "NAS" ], "minimalOrder": { - "amount": 0.01, + "amount": 0.1, "price": 0.00001, "order": 1 } @@ -2521,7 +2591,7 @@ "NAV" ], "minimalOrder": { - "amount": 0.01, + "amount": 0.1, "price": 0.00001, "order": 1 } @@ -2587,7 +2657,7 @@ "NEBL" ], "minimalOrder": { - "amount": 0.01, + "amount": 0.1, "price": 0.00001, "order": 1 } @@ -2658,6 +2728,17 @@ "order": 0.01 } }, + { + "pair": [ + "USDT", + "NULS" + ], + "minimalOrder": { + "amount": 0.01, + "price": 0.0001, + "order": 10 + } + }, { "pair": [ "BNB", @@ -2708,7 +2789,7 @@ "ONT" ], "minimalOrder": { - "amount": 0.01, + "amount": 0.1, "price": 0.00001, "order": 1 } @@ -2785,7 +2866,7 @@ "PIVX" ], "minimalOrder": { - "amount": 0.01, + "amount": 0.1, "price": 0.00001, "order": 1 } @@ -2818,7 +2899,7 @@ "POA" ], "minimalOrder": { - "amount": 0.01, + "amount": 0.1, "price": 0.00001, "order": 1 } @@ -2867,13 +2948,35 @@ "order": 0.01 } }, + { + "pair": [ + "BNB", + "POLY" + ], + "minimalOrder": { + "amount": 0.1, + "price": 0.00001, + "order": 1 + } + }, + { + "pair": [ + "BTC", + "POLY" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, { "pair": [ "BNB", "POWR" ], "minimalOrder": { - "amount": 0.01, + "amount": 0.1, "price": 0.00001, "order": 1 } @@ -3016,7 +3119,7 @@ "QTUM" ], "minimalOrder": { - "amount": 0.01, + "amount": 0.1, "price": 0.00001, "order": 1 } @@ -3071,7 +3174,7 @@ "RDN" ], "minimalOrder": { - "amount": 0.01, + "amount": 0.1, "price": 0.00001, "order": 1 } @@ -3159,7 +3262,7 @@ "RLC" ], "minimalOrder": { - "amount": 0.01, + "amount": 0.1, "price": 0.00001, "order": 1 } @@ -3302,7 +3405,7 @@ "STEEM" ], "minimalOrder": { - "amount": 0.01, + "amount": 0.1, "price": 0.00001, "order": 1 } @@ -3412,7 +3515,7 @@ "SYS" ], "minimalOrder": { - "amount": 0.01, + "amount": 0.1, "price": 0.00001, "order": 1 } @@ -3445,7 +3548,7 @@ "THETA" ], "minimalOrder": { - "amount": 0.01, + "amount": 0.1, "price": 0.00001, "order": 1 } @@ -3522,7 +3625,7 @@ "TRIG" ], "minimalOrder": { - "amount": 0.01, + "amount": 0.1, "price": 0.00001, "order": 1 } @@ -3588,7 +3691,7 @@ "TUSD" ], "minimalOrder": { - "amount": 0.01, + "amount": 0.1, "price": 0.00001, "order": 1 } @@ -3626,10 +3729,21 @@ "order": 10 } }, + { + "pair": [ + "BNB", + "VET" + ], + "minimalOrder": { + "amount": 0.1, + "price": 0.00001, + "order": 1 + } + }, { "pair": [ "BTC", - "VEN" + "VET" ], "minimalOrder": { "amount": 1, @@ -3640,7 +3754,7 @@ { "pair": [ "ETH", - "VEN" + "VET" ], "minimalOrder": { "amount": 1, @@ -3651,11 +3765,11 @@ { "pair": [ "USDT", - "VEN" + "VET" ], "minimalOrder": { - "amount": 0.01, - "price": 0.0001, + "amount": 0.1, + "price": 0.00001, "order": 10 } }, @@ -3665,7 +3779,7 @@ "VIA" ], "minimalOrder": { - "amount": 0.01, + "amount": 0.1, "price": 0.00001, "order": 1 } @@ -3742,7 +3856,7 @@ "WABI" ], "minimalOrder": { - "amount": 0.01, + "amount": 0.1, "price": 0.00001, "order": 1 } @@ -3775,7 +3889,7 @@ "WAN" ], "minimalOrder": { - "amount": 0.01, + "amount": 0.1, "price": 0.00001, "order": 1 } @@ -3896,7 +4010,7 @@ "XEM" ], "minimalOrder": { - "amount": 0.01, + "amount": 0.1, "price": 0.00001, "order": 1 } @@ -3929,7 +4043,7 @@ "XLM" ], "minimalOrder": { - "amount": 0.01, + "amount": 0.1, "price": 0.00001, "order": 1 } @@ -3962,7 +4076,7 @@ "XLM" ], "minimalOrder": { - "amount": 0.01, + "amount": 0.1, "price": 0.00001, "order": 10 } @@ -3995,7 +4109,7 @@ "XRP" ], "minimalOrder": { - "amount": 0.01, + "amount": 0.1, "price": 0.00001, "order": 1 } From 5d3ff65f2d0ffb2d01ea6267b34b7d2d24149bd4 Mon Sep 17 00:00:00 2001 From: Mike van Rossum Date: Thu, 9 Aug 2018 15:59:23 +0800 Subject: [PATCH 04/29] [GB] point to supported_exchanges doc --- docs/gekko-broker/introduction.md | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/docs/gekko-broker/introduction.md b/docs/gekko-broker/introduction.md index e0a425c76..86745587f 100644 --- a/docs/gekko-broker/introduction.md +++ b/docs/gekko-broker/introduction.md @@ -30,19 +30,7 @@ This library allows you to: ## Status -Early WIP. All communication is via the REST APIs of exchanges. Not all exchanges are supported. - -Currently fully supported exchanges: - -- Binance -- GDAX -- Poloniex -- Coinfalcon -- Kraken - -Currently exchanges with limited support: - -- bittrex +Early WIP. All communication is via the REST APIs of exchanges. Not all exchanges are supported, see which ones are in [this doc](../introduction/supported_exchanges.md). ## Order types From c0a96f9139a3f4ff82af83fb3ca968879115a99e Mon Sep 17 00:00:00 2001 From: Mike van Rossum Date: Thu, 9 Aug 2018 16:10:18 +0800 Subject: [PATCH 05/29] [GB] add example usage, fix #2414 --- docs/gekko-broker/introduction.md | 72 +++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/docs/gekko-broker/introduction.md b/docs/gekko-broker/introduction.md index 86745587f..71f777086 100644 --- a/docs/gekko-broker/introduction.md +++ b/docs/gekko-broker/introduction.md @@ -50,6 +50,78 @@ TODO: - Stop - If Touched (stop but opposite direction) +### Example + +Set up a Gekko Broker instance: + + // from the gekko repo (make sure you have deps installed + // inside the exchange folder). + const Broker = require('../gekko/exchange/GekkoBroker'); + // or from NPM + // const Broker = require('gekko-broker'); + + const binance = new Broker({ + currency: 'USDT', + asset: 'BTC', + private: true, + + exchange: 'binance', + key: 'x', // add your API key + secret: 'y' // add your API secret + }); + +Now we have an instance that can create a [sticky order](./sticky_order.md): + + const type = 'sticky'; + const side = 'buy'; + const amount = 1; + + const order = binance.createOrder(type, side, amount); + + order.on('statusChange', s => console.log(now(), 'new status', s)); + order.on('fill', s => console.log(now(), 'filled', s)); + order.on('error', s => console.log(now(), 'error!', e)); + order.on('completed', a => { + console.log(new Date, 'completed!'); + order.createSummary((err, s) => { + console.log(new Date, 'summary:'); + console.log(JSON.stringify(s, null, 2)); + }); + }); + +This one doesn't have an upper limit price for what it will buy at. It will stick it's bid offer at BBO until it's filled. If you have a limit in mind you can specify it when creating, do this instead: + + const order = binance.createOrder(type, side, amount, { limit: 100 }); + +It will never offer to buy for more than 100, even if the BBO is above 100 (the bid will end up deep in the book). + +At any point in time you can change the limit (or the amount), for example: + + order.moveLimit(120); + +Running the above example (without setting a limit and moving it) will yield: + + root@foxtail:~/gb/nusadua# node b + 2018-07-29 03:46:02 new status SUBMITTED + 2018-07-29 03:46:02 new status OPEN + 2018-07-29 03:46:04 filled 1 + 2018-07-29 03:46:04 new status FILLED + 2018-07-29T03:46:04.127Z 'completed!' + 2018-07-29T03:46:04.358Z 'summary:' + { + "price": 0.0017479, + "amount": 1, + "date": "2018-07-29T03:46:02.576Z", + "side": "buy", + "orders": 1, + "fees": { + "BNB": 0.00075 + }, + "feePercent": 0.075 + } + +NOTE: not all status changes are documented and more events are planned but not implemented. + ### TODO - finish all exchange integrations that gekko supports From 5df2ad2f20f8f767efa4e3e2597b5704ace31dd8 Mon Sep 17 00:00:00 2001 From: Mike van Rossum Date: Thu, 9 Aug 2018 16:21:30 +0800 Subject: [PATCH 06/29] assume CF maintainance to be recoverable, fix #2407 --- exchange/wrappers/coinfalcon.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/exchange/wrappers/coinfalcon.js b/exchange/wrappers/coinfalcon.js index ca03977f4..92f82594a 100644 --- a/exchange/wrappers/coinfalcon.js +++ b/exchange/wrappers/coinfalcon.js @@ -49,7 +49,9 @@ const recoverableErrors = [ '408', // "The timestamp 1527996378 is invalid, current timestamp is 1527996441." 'is invalid, current timestamp is', - 'EHOSTUNREACH' + 'EHOSTUNREACH', + // https://github.com/askmike/gekko/issues/2407 + 'We are fixing a few issues, be back shortly.' ]; Trader.prototype.processResponse = function(method, args, next) { From 214cedfbe377d516b8cd09b6f3dcb5bd0189d273 Mon Sep 17 00:00:00 2001 From: Mike van Rossum Date: Fri, 10 Aug 2018 17:30:11 +0800 Subject: [PATCH 07/29] use npm version of of forked bittrex dep --- exchange/package-lock.json | 336 ++++++++++++++++++++----------------- exchange/package.json | 2 +- 2 files changed, 181 insertions(+), 157 deletions(-) diff --git a/exchange/package-lock.json b/exchange/package-lock.json index 5c41e1864..faaaf981d 100644 --- a/exchange/package-lock.json +++ b/exchange/package-lock.json @@ -14,13 +14,13 @@ "resolved": "https://registry.npmjs.org/@types/form-data/-/form-data-2.2.1.tgz", "integrity": "sha512-JAMFhOaHIciYVh8fb5/83nmuO/AHwmto+Hq7a9y8FzLDcC1KCU344XDOMEmahnrTFlHjgh4L0WJFczNIX2GxnQ==", "requires": { - "@types/node": "10.1.2" + "@types/node": "10.5.7" } }, "@types/node": { - "version": "10.1.2", - "resolved": "https://registry.npmjs.org/@types/node/-/node-10.1.2.tgz", - "integrity": "sha512-bjk1RIeZBCe/WukrFToIVegOf91Pebr8cXYBwLBIsfiGWVQ+ifwWsT59H3RxrWzWrzd1l/Amk1/ioY5Fq3/bpA==" + "version": "10.5.7", + "resolved": "https://registry.npmjs.org/@types/node/-/node-10.5.7.tgz", + "integrity": "sha512-VkKcfuitP+Nc/TaTFH0B8qNmn+6NbI6crLkQonbedViVz7O2w8QV/GERPlkJ4bg42VGHiEWa31CoTOPs1q6z1w==" }, "@types/request": { "version": "2.47.0", @@ -29,7 +29,7 @@ "requires": { "@types/caseless": "0.12.1", "@types/form-data": "2.2.1", - "@types/node": "10.1.2", + "@types/node": "10.5.7", "@types/tough-cookie": "2.3.3" } }, @@ -50,9 +50,12 @@ } }, "asn1": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz", - "integrity": "sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y=" + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", + "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", + "requires": { + "safer-buffer": "2.1.2" + } }, "assert-plus": { "version": "1.0.0", @@ -60,9 +63,9 @@ "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" }, "async": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/async/-/async-2.6.0.tgz", - "integrity": "sha512-xAfGg1/NTLBBKlHFmnd7PlmUW9KhVQIUuSrYem9xzFUZy13ScvtyGGejaae9iAVRiRq9+Cx7DPFaAAhCpyxyPw==", + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.1.tgz", + "integrity": "sha512-fNEiL2+AZt6AlAw/29Cr0UDe4sRAHCpEHh54WMz+Bb7QfNcFw4h3loofyJpLeQs4Yx7yuqu/2dLgM5hKOs6HlQ==", "requires": { "lodash": "4.17.10" } @@ -83,23 +86,23 @@ "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=" }, "aws4": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.7.0.tgz", - "integrity": "sha512-32NDda82rhwD9/JBCCkB+MRYDp0oSvlo2IL6rQWA10PQi7tDUM3eqMSltXmY+Oyl/7N3P3qNtAlv7X0d9bI28w==" + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz", + "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==" }, "babel-runtime": { "version": "6.26.0", "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", "requires": { - "core-js": "2.5.6", + "core-js": "2.5.7", "regenerator-runtime": "0.11.1" } }, "bcrypt-pbkdf": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz", - "integrity": "sha1-Y7xdy2EzG5K8Bf1SiVPDNGKgb40=", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", "optional": true, "requires": { "tweetnacl": "0.14.5" @@ -115,8 +118,8 @@ "resolved": "https://registry.npmjs.org/binance/-/binance-1.3.3.tgz", "integrity": "sha512-1eV2QUoH/Z0FZPiGjigJg4udXV9Uu6Clr0Sg1xsX3xStgPfzXz0juA3mllQIiIaHx7dmfAQgEiZIyeJLx5ajag==", "requires": { - "request": "2.86.0", - "underscore": "1.9.0", + "request": "2.87.0", + "underscore": "1.9.1", "ws": "3.3.3" } }, @@ -136,7 +139,7 @@ "debug": "2.6.9", "lodash": "4.17.10", "lodash.throttle": "4.1.1", - "request": "2.86.0", + "request": "2.87.0", "request-promise": "4.2.2", "ws": "3.3.3" } @@ -152,11 +155,11 @@ "integrity": "sha512-MKiLiV+I1AA596t9w1sQJ8jkiSr5+ZKi0WKrYGUn6d1Fx+Ij4tIj+m2WMQSGczs5jZVxV339chE8iwk6F64wjA==" }, "boom": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/boom/-/boom-4.3.1.tgz", - "integrity": "sha1-T4owBctKfjiJ90kDD9JbluAdLjE=", + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/boom/-/boom-0.4.2.tgz", + "integrity": "sha1-emNune1O/O+xnO9JR6PGffrukRs=", "requires": { - "hoek": "4.2.1" + "hoek": "0.9.1" } }, "caseless": { @@ -169,15 +172,23 @@ "resolved": "https://registry.npmjs.org/cbq/-/cbq-0.0.1.tgz", "integrity": "sha512-MCLjfpHAcI3gPdx26xoQx2JqRdNMf68ovhJkqNSiZIx/yQP6yNYYKbf8ww2J6kgNTPGIrXyugFlNz5jmGtg8BQ==" }, + "cloudscraper": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/cloudscraper/-/cloudscraper-1.5.0.tgz", + "integrity": "sha512-bZagLhj59+N6Z6lD9zRksYu87GthLwXdKARULi4RZ6UVpotH39ruSFN3UQmw3uuqoj00iDxkGrapAvxeurmlQA==", + "requires": { + "request": "2.87.0" + } + }, "co": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=" }, "coinfalcon": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/coinfalcon/-/coinfalcon-1.0.3.tgz", - "integrity": "sha512-dzyLdeDGY9Fg4zewCFolK/TjB/Mrf9tpBupx7IAqhZcYH6jY5z7xxMywIgJnf4bbRKMIEnJ2GJFqgue9M1nwnw==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/coinfalcon/-/coinfalcon-1.0.5.tgz", + "integrity": "sha512-TyzLmcE2Vmll6oCyZSdEkla/thVZPjLxhDwlPyYKB5/Uv5wf/dzwox3Q2DrnJcOKRAQ1bKcHWmR2dFQKaEoK+A==", "requires": { "babel-runtime": "6.26.0" } @@ -191,9 +202,9 @@ } }, "core-js": { - "version": "2.5.6", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.5.6.tgz", - "integrity": "sha512-lQUVfQi0aLix2xpyjrrJEvfuYCqPc/HwmTKsC/VNf8q0zsjX7SQZtp4+oRONN5Tsur9GDETPjj+Ub2iDiGZfSQ==" + "version": "2.5.7", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.5.7.tgz", + "integrity": "sha512-RszJCAxg/PP6uzXVXL6BsxSXx/B05oJAQ2vkJRjyjrEcNVycaqOmNb5OTxZPE3xa5gwZduqza6L9JOCenh/Ecw==" }, "core-util-is": { "version": "1.0.2", @@ -210,21 +221,12 @@ } }, "cryptiles": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-3.1.2.tgz", - "integrity": "sha1-qJ+7Ig9c4l7FboxKqKT9e1sNKf4=", + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-0.2.2.tgz", + "integrity": "sha1-7ZH/HxetE9N0gohZT4pIoNJvMlw=", + "optional": true, "requires": { - "boom": "5.2.0" - }, - "dependencies": { - "boom": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/boom/-/boom-5.2.0.tgz", - "integrity": "sha512-Z5BTk6ZRe4tXXQlkqftmsAUANpXmuwlsF5Oov8ThoMbQRzdGTA1ngYRW160GexgOgjsFOKJz0LYhoNi+2AMBUw==", - "requires": { - "hoek": "4.2.1" - } - } + "boom": "0.4.2" } }, "ctype": { @@ -254,7 +256,7 @@ "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=", "requires": { - "mimic-response": "1.0.0" + "mimic-response": "1.0.1" } }, "delayed-stream": { @@ -268,12 +270,13 @@ "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=" }, "ecc-jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz", - "integrity": "sha1-D8c6ntXw1Tw4GTOYUj735UN3dQU=", + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", "optional": true, "requires": { - "jsbn": "0.1.1" + "jsbn": "0.1.1", + "safer-buffer": "2.1.2" } }, "exit-on-epipe": { @@ -282,9 +285,9 @@ "integrity": "sha512-h2z5mrROTxce56S+pnvAV890uu7ls7f1kEvVGJbw1OlFH3/mlJ5bkXu0KRyW94v37zzHPiUd55iLn3DA7TjWpw==" }, "extend": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz", - "integrity": "sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ=" + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" }, "extsprintf": { "version": "1.3.0", @@ -313,7 +316,7 @@ "requires": { "asynckit": "0.4.0", "combined-stream": "1.0.6", - "mime-types": "2.1.18" + "mime-types": "2.1.19" } }, "gdax": { @@ -324,7 +327,7 @@ "@types/request": "2.47.0", "bignumber.js": "5.0.0", "bintrees": "1.0.2", - "request": "2.86.0", + "request": "2.87.0", "ws": "4.1.0" }, "dependencies": { @@ -344,6 +347,18 @@ } } }, + "gekko-bittrex": { + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/gekko-bittrex/-/gekko-bittrex-0.8.5.tgz", + "integrity": "sha512-yA4FV1MjVXIX5/vpKGtZnh28OG9Mj+R2Dcb9v71OBPan6uHw4HYfX8gM6RBwlLmiVGxhyg5uDsHBiYab9yr86A==", + "requires": { + "cloudscraper": "1.5.0", + "jsonic": "0.3.0", + "object-assign": "4.1.1", + "request": "2.87.0", + "signalr-client": "0.0.17" + } + }, "get-stream": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", @@ -406,20 +421,21 @@ } }, "hawk": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/hawk/-/hawk-6.0.2.tgz", - "integrity": "sha512-miowhl2+U7Qle4vdLqDdPt9m09K6yZhkLDTWGoUiUzrQCn+mHHSmfJgAyGaLRZbPmTqfFFjRV1QWCW0VWUJBbQ==", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/hawk/-/hawk-1.0.0.tgz", + "integrity": "sha1-uQuxaYByhUEdp//LjdJZhQLTtS0=", + "optional": true, "requires": { - "boom": "4.3.1", - "cryptiles": "3.1.2", - "hoek": "4.2.1", - "sntp": "2.1.0" + "boom": "0.4.2", + "cryptiles": "0.2.2", + "hoek": "0.9.1", + "sntp": "0.2.4" } }, "hoek": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/hoek/-/hoek-4.2.1.tgz", - "integrity": "sha512-QLg82fGkfnJ/4iy1xZ81/9SIJiq1NGFUMGs6ParyjBZr6jW2Ufj/snDqTHixNlHdPNwN2RLVD0Pi3igeK9+JfA==" + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/hoek/-/hoek-0.9.1.tgz", + "integrity": "sha1-PTIkYrrfB3Fup+uFuviAec3c5QU=" }, "http-signature": { "version": "1.2.0", @@ -428,7 +444,7 @@ "requires": { "assert-plus": "1.0.0", "jsprim": "1.4.1", - "sshpk": "1.14.1" + "sshpk": "1.14.2" } }, "is-object": { @@ -491,6 +507,11 @@ "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" }, + "jsonic": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/jsonic/-/jsonic-0.3.0.tgz", + "integrity": "sha1-tUXalfVDkuWLPdoF9fLjd6bJ0b8=" + }, "jsprim": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", @@ -532,33 +553,38 @@ "integrity": "sha1-WCA+7Ybjpe8XrtK32evUfwpg3RA=" }, "mime-db": { - "version": "1.33.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.33.0.tgz", - "integrity": "sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ==" + "version": "1.35.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.35.0.tgz", + "integrity": "sha512-JWT/IcCTsB0Io3AhWUMjRqucrHSPsSf2xKLaRldJVULioggvkJvggZ3VXNNSRkCddE6D+BUI4HEIZIA2OjwIvg==" }, "mime-types": { - "version": "2.1.18", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.18.tgz", - "integrity": "sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==", + "version": "2.1.19", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.19.tgz", + "integrity": "sha512-P1tKYHVSZ6uFo26mtnve4HQFE3koh1UWVkp8YUC+ESBHe945xWSoXuHHiGarDqcEZ+whpCDnlNw5LON0kLo+sw==", "requires": { - "mime-db": "1.33.0" + "mime-db": "1.35.0" } }, "mimic-response": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.0.tgz", - "integrity": "sha1-3z02Uqc/3ta5sLJBRub9BSNTRY4=" + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", + "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==" }, "moment": { - "version": "2.22.1", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.22.1.tgz", - "integrity": "sha512-shJkRTSebXvsVqk56I+lkb2latjBs8I+pc2TzWc545y2iFnSjm7Wg0QMh+ZWcdSLQyGEau5jI8ocnmkyTgr9YQ==" + "version": "2.22.2", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.22.2.tgz", + "integrity": "sha1-PCV/mDn8DpP/UxSWMiOeuQeD/2Y=" }, "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" }, + "nan": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.10.0.tgz", + "integrity": "sha512-bAdJv7fBLhWC+/Bls0Oza+mvTaNQtP+1RyhhhvD95pgUJz6XM5IzgmxOkItJ9tkoCiplvAnXI1tNmmUD/eScyA==" + }, "nonce": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/nonce/-/nonce-1.0.4.tgz", @@ -569,6 +595,11 @@ "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz", "integrity": "sha1-Rqarfwrq2N6unsBWV4C31O/rnUM=" }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" + }, "p-cancelable": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-0.3.0.tgz", @@ -623,14 +654,6 @@ "integrity": "sha1-xXED96F/wDfwLXwuZLYC6iI/fWM=", "optional": true }, - "boom": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/boom/-/boom-0.4.2.tgz", - "integrity": "sha1-emNune1O/O+xnO9JR6PGffrukRs=", - "requires": { - "hoek": "0.9.1" - } - }, "combined-stream": { "version": "0.0.7", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-0.0.7.tgz", @@ -640,15 +663,6 @@ "delayed-stream": "0.0.5" } }, - "cryptiles": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-0.2.2.tgz", - "integrity": "sha1-7ZH/HxetE9N0gohZT4pIoNJvMlw=", - "optional": true, - "requires": { - "boom": "0.4.2" - } - }, "delayed-stream": { "version": "0.0.5", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-0.0.5.tgz", @@ -671,23 +685,6 @@ "mime": "1.2.11" } }, - "hawk": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/hawk/-/hawk-1.0.0.tgz", - "integrity": "sha1-uQuxaYByhUEdp//LjdJZhQLTtS0=", - "optional": true, - "requires": { - "boom": "0.4.2", - "cryptiles": "0.2.2", - "hoek": "0.9.1", - "sntp": "0.2.4" - } - }, - "hoek": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/hoek/-/hoek-0.9.1.tgz", - "integrity": "sha1-PTIkYrrfB3Fup+uFuviAec3c5QU=" - }, "http-signature": { "version": "0.10.1", "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-0.10.1.tgz", @@ -734,15 +731,6 @@ "tunnel-agent": "0.3.0" } }, - "sntp": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/sntp/-/sntp-0.2.4.tgz", - "integrity": "sha1-+4hfGLDzqtGJ+CSGJTa87ux1CQA=", - "optional": true, - "requires": { - "hoek": "0.9.1" - } - }, "tunnel-agent": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.3.0.tgz", @@ -777,31 +765,30 @@ "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==" }, "request": { - "version": "2.86.0", - "resolved": "https://registry.npmjs.org/request/-/request-2.86.0.tgz", - "integrity": "sha512-BQZih67o9r+Ys94tcIW4S7Uu8pthjrQVxhsZ/weOwHbDfACxvIyvnAbzFQxjy1jMtvFSzv5zf4my6cZsJBbVzw==", + "version": "2.87.0", + "resolved": "https://registry.npmjs.org/request/-/request-2.87.0.tgz", + "integrity": "sha512-fcogkm7Az5bsS6Sl0sibkbhcKsnyon/jV1kF3ajGmF0c8HrttdKTPRT9hieOaQHA5HEq6r8OyWOo/o781C1tNw==", "requires": { "aws-sign2": "0.7.0", - "aws4": "1.7.0", + "aws4": "1.8.0", "caseless": "0.12.0", "combined-stream": "1.0.6", - "extend": "3.0.1", + "extend": "3.0.2", "forever-agent": "0.6.1", "form-data": "2.3.2", "har-validator": "5.0.3", - "hawk": "6.0.2", "http-signature": "1.2.0", "is-typedarray": "1.0.0", "isstream": "0.1.2", "json-stringify-safe": "5.0.1", - "mime-types": "2.1.18", + "mime-types": "2.1.19", "oauth-sign": "0.8.2", "performance-now": "2.1.0", "qs": "6.5.2", "safe-buffer": "5.1.2", "tough-cookie": "2.3.4", "tunnel-agent": "0.6.0", - "uuid": "3.2.1" + "uuid": "3.3.2" } }, "request-promise": { @@ -813,16 +800,14 @@ "request-promise-core": "1.1.1", "stealthy-require": "1.1.1", "tough-cookie": "2.3.4" - }, - "dependencies": { - "request-promise-core": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.1.tgz", - "integrity": "sha1-Pu4AssWqgyOc+wTFcA2jb4HNCLY=", - "requires": { - "lodash": "4.17.10" - } - } + } + }, + "request-promise-core": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.1.tgz", + "integrity": "sha1-Pu4AssWqgyOc+wTFcA2jb4HNCLY=", + "requires": { + "lodash": "4.17.10" } }, "retry": { @@ -835,26 +820,41 @@ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "signalr-client": { + "version": "0.0.17", + "resolved": "https://registry.npmjs.org/signalr-client/-/signalr-client-0.0.17.tgz", + "integrity": "sha1-pSF383ziSOzIcibdEDxB/3DIKbE=", + "requires": { + "websocket": "1.0.26" + } + }, "sntp": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/sntp/-/sntp-2.1.0.tgz", - "integrity": "sha512-FL1b58BDrqS3A11lJ0zEdnJ3UOKqVxawAkF3k7F0CVN7VQ34aZrV+G8BZ1WC9ZL7NyrwsW0oviwsWDgRuVYtJg==", + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/sntp/-/sntp-0.2.4.tgz", + "integrity": "sha1-+4hfGLDzqtGJ+CSGJTa87ux1CQA=", + "optional": true, "requires": { - "hoek": "4.2.1" + "hoek": "0.9.1" } }, "sshpk": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.14.1.tgz", - "integrity": "sha1-Ew9Zde3a2WPx1W+SuaxsUfqfg+s=", + "version": "1.14.2", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.14.2.tgz", + "integrity": "sha1-xvxhZIo9nE52T9P8306hBeSSupg=", "requires": { - "asn1": "0.2.3", + "asn1": "0.2.4", "assert-plus": "1.0.0", - "bcrypt-pbkdf": "1.0.1", + "bcrypt-pbkdf": "1.0.2", "dashdash": "1.14.1", - "ecc-jsbn": "0.1.1", + "ecc-jsbn": "0.1.2", "getpass": "0.1.7", "jsbn": "0.1.1", + "safer-buffer": "2.1.2", "tweetnacl": "0.14.5" } }, @@ -890,15 +890,23 @@ "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", "optional": true }, + "typedarray-to-buffer": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", + "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", + "requires": { + "is-typedarray": "1.0.0" + } + }, "ultron": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ultron/-/ultron-1.1.1.tgz", "integrity": "sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og==" }, "underscore": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.9.0.tgz", - "integrity": "sha512-4IV1DSSxC1QK48j9ONFK1MoIAKKkbE8i7u55w2R6IqBqbT7A/iG7aZBCR2Bi8piF0Uz+i/MG1aeqLwl/5vqF+A==" + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.9.1.tgz", + "integrity": "sha512-5/4etnCkd9c8gwgowi5/om/mYO5ajCaOgdzj/oW+0eQV9WxKBDZw5+ycmKmeaTXjInS/W0BzpGLo2xR2aBwZdg==" }, "url-parse-lax": { "version": "1.0.0", @@ -914,9 +922,9 @@ "integrity": "sha1-FQWgOiiaSMvXpDTvuu7FBV9WM6k=" }, "uuid": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.2.1.tgz", - "integrity": "sha512-jZnMwlb9Iku/O3smGWvZhauCf6cvvpKi4BKRiliS3cxnI+Gz9j5MEpTz2UFuXiKPJocb7gnsLHwiS05ige5BEA==" + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", + "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==" }, "verror": { "version": "1.10.0", @@ -928,6 +936,17 @@ "extsprintf": "1.3.0" } }, + "websocket": { + "version": "1.0.26", + "resolved": "https://registry.npmjs.org/websocket/-/websocket-1.0.26.tgz", + "integrity": "sha512-fjcrYDPIQxpTnqFQ9JjxUQcdvR89MFAOjPBlF+vjOt49w/XW4fJknUoMz/mDIn2eK1AdslVojcaOxOqyZZV8rw==", + "requires": { + "debug": "2.6.9", + "nan": "2.10.0", + "typedarray-to-buffer": "3.1.5", + "yaeti": "0.0.6" + } + }, "ws": { "version": "3.3.3", "resolved": "https://registry.npmjs.org/ws/-/ws-3.3.3.tgz", @@ -937,6 +956,11 @@ "safe-buffer": "5.1.2", "ultron": "1.1.1" } + }, + "yaeti": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/yaeti/-/yaeti-0.0.6.tgz", + "integrity": "sha1-8m9ITXJoTPQr7ft2lwqhYI+/lXc=" } } } diff --git a/exchange/package.json b/exchange/package.json index 1cba2d0cd..ebf2d6394 100644 --- a/exchange/package.json +++ b/exchange/package.json @@ -33,6 +33,6 @@ "poloniex.js": "git://github.com/askmike/poloniex.js.git#69f5e254353e66d135070844fc3328efcbe3641c", "request-promise": "^4.2.2", "retry": "^0.12.0", - "node.bittrex.api": "git://github.com/askmike/poloniex.js.git#69f5e254353e66d135070844fc3328efcbe3641c" + "gekko-bittrex": "^0.8.5" } } From 01212c2c23adf729c4787b66011798e42afdef1a Mon Sep 17 00:00:00 2001 From: Mike van Rossum Date: Fri, 10 Aug 2018 17:30:41 +0800 Subject: [PATCH 08/29] catch gb init errors --- plugins/trader/trader.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/plugins/trader/trader.js b/plugins/trader/trader.js index 97c28c0aa..2ce53b198 100644 --- a/plugins/trader/trader.js +++ b/plugins/trader/trader.js @@ -19,7 +19,11 @@ const Trader = function(next) { this.propogatedTrades = 0; - this.broker = new Broker(this.brokerConfig); + try { + this.broker = new Broker(this.brokerConfig); + } catch(e) { + util.die(e.message); + } if(!this.broker.capabilities.gekkoBroker) { util.die('This exchange is not yet supported'); From 32b4cbea3c77bbbe48668f34a0a14bf7e40ae0f4 Mon Sep 17 00:00:00 2001 From: Mike van Rossum Date: Fri, 10 Aug 2018 21:21:24 +0800 Subject: [PATCH 09/29] [sqlite] wait for 10 seconds on busy --- plugins/sqlite/handle.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/sqlite/handle.js b/plugins/sqlite/handle.js index fd25b490e..93832ee4f 100644 --- a/plugins/sqlite/handle.js +++ b/plugins/sqlite/handle.js @@ -52,7 +52,7 @@ module.exports = { var db = new sqlite3.Database(fullPath); db.run('PRAGMA synchronous = ' + syncMode); db.run('PRAGMA journal_mode = ' + journalMode); - db.configure('busyTimeout', 1500); + db.configure('busyTimeout', 10000); return db; } }; From 51a7ce3960b955c09816d171f049ef4d038d9cc7 Mon Sep 17 00:00:00 2001 From: Mike van Rossum Date: Fri, 10 Aug 2018 20:45:36 +0800 Subject: [PATCH 10/29] throw errors, not strings --- core/budfox/marketFetcher.js | 2 +- core/budfox/tradeBatcher.js | 4 ++-- core/candleBatcher.js | 7 ++++--- core/talib.js | 10 ++++++---- core/tulind.js | 10 ++++++---- 5 files changed, 19 insertions(+), 14 deletions(-) diff --git a/core/budfox/marketFetcher.js b/core/budfox/marketFetcher.js index 3e0c5e944..2e6c6575d 100644 --- a/core/budfox/marketFetcher.js +++ b/core/budfox/marketFetcher.js @@ -20,7 +20,7 @@ const TradeBatcher = require(util.dirs().budfox + 'tradeBatcher'); const Fetcher = function(config) { if(!_.isObject(config)) - throw 'TradeFetcher expects a config'; + throw new Error('TradeFetcher expects a config'); const exchangeName = config.watch.exchange.toLowerCase(); const DataProvider = require(util.dirs().gekko + 'exchange/wrappers/' + exchangeName); diff --git a/core/budfox/tradeBatcher.js b/core/budfox/tradeBatcher.js index b6b5e13d1..20bdb16cb 100644 --- a/core/budfox/tradeBatcher.js +++ b/core/budfox/tradeBatcher.js @@ -37,7 +37,7 @@ var log = require('../log'); var TradeBatcher = function(tid) { if(!_.isString(tid)) - throw 'tid is not a string'; + throw new Error('tid is not a string'); _.bindAll(this); this.tid = tid; @@ -49,7 +49,7 @@ util.makeEventEmitter(TradeBatcher); TradeBatcher.prototype.write = function(batch) { if(!_.isArray(batch)) - throw 'batch is not an array'; + throw new Error('batch is not an array'); if(_.isEmpty(batch)) return log.debug('Trade fetch came back empty.'); diff --git a/core/candleBatcher.js b/core/candleBatcher.js index dd64d456b..0cb5ade07 100644 --- a/core/candleBatcher.js +++ b/core/candleBatcher.js @@ -14,7 +14,7 @@ var util = require(__dirname + '/util'); var CandleBatcher = function(candleSize) { if(!_.isNumber(candleSize)) - throw 'candleSize is not a number'; + throw new Error('candleSize is not a number'); this.candleSize = candleSize; this.smallCandles = []; @@ -26,8 +26,9 @@ var CandleBatcher = function(candleSize) { util.makeEventEmitter(CandleBatcher); CandleBatcher.prototype.write = function(candles) { - if(!_.isArray(candles)) - throw 'candles is not an array'; + if(!_.isArray(candles)) { + throw new Error('candles is not an array'); + } this.emitted = 0; diff --git a/core/talib.js b/core/talib.js index 117da8b1f..38fdaaccc 100644 --- a/core/talib.js +++ b/core/talib.js @@ -36,13 +36,15 @@ var verifyParams = (methodName, params) => { var requiredParams = methods[methodName].requires; _.each(requiredParams, paramName => { - if(!_.has(params, paramName)) - throw talibError + methodName + ' requires ' + paramName + '.'; + if(!_.has(params, paramName)) { + throw new Error(talibError + methodName + ' requires ' + paramName + '.'); + } var val = params[paramName]; - if(!_.isNumber(val)) - throw talibError + paramName + ' needs to be a number'; + if(!_.isNumber(val)) { + throw new Error(talibError + paramName + ' needs to be a number'); + } }); } diff --git a/core/tulind.js b/core/tulind.js index a46a5fb80..04b6766ff 100644 --- a/core/tulind.js +++ b/core/tulind.js @@ -32,13 +32,15 @@ var verifyParams = (methodName, params) => { var requiredParams = methods[methodName].requires; _.each(requiredParams, paramName => { - if(!_.has(params, paramName)) - throw tulindError + methodName + ' requires ' + paramName + '.'; + if(!_.has(params, paramName)) { + throw new Error(tulindError + methodName + ' requires ' + paramName + '.'); + } var val = params[paramName]; - if(!_.isNumber(val)) - throw tulindError + paramName + ' needs to be a number'; + if(!_.isNumber(val)) { + throw new Error(tulindError + paramName + ' needs to be a number'); + } }); } From 74934669c9a63836c537761ec45e8c8b02a59d9c Mon Sep 17 00:00:00 2001 From: Mike van Rossum Date: Fri, 10 Aug 2018 21:38:38 +0800 Subject: [PATCH 11/29] [GB] update bfx & cf markets --- exchange/wrappers/bitfinex-markets.json | 508 ++++++++++++++-------- exchange/wrappers/coinfalcon-markets.json | 480 -------------------- 2 files changed, 332 insertions(+), 656 deletions(-) diff --git a/exchange/wrappers/bitfinex-markets.json b/exchange/wrappers/bitfinex-markets.json index 307168756..670be0cd2 100644 --- a/exchange/wrappers/bitfinex-markets.json +++ b/exchange/wrappers/bitfinex-markets.json @@ -81,7 +81,13 @@ "ATM", "HOT", "DTA", - "IQX" + "IQX", + "WPR", + "ZIL", + "BNT", + "ABS", + "XRA", + "MAN" ], "currencies": [ "USD", @@ -209,7 +215,7 @@ "XMR" ], "minimalOrder": { - "amount": "0.1", + "amount": "0.2", "unit": "asset" } }, @@ -219,7 +225,7 @@ "XMR" ], "minimalOrder": { - "amount": "0.1", + "amount": "0.2", "unit": "asset" } }, @@ -229,7 +235,7 @@ "DSH" ], "minimalOrder": { - "amount": "0.04", + "amount": "0.06", "unit": "asset" } }, @@ -239,7 +245,7 @@ "DSH" ], "minimalOrder": { - "amount": "0.04", + "amount": "0.06", "unit": "asset" } }, @@ -269,7 +275,7 @@ "XRP" ], "minimalOrder": { - "amount": "22.0", + "amount": "30.0", "unit": "asset" } }, @@ -279,7 +285,7 @@ "XRP" ], "minimalOrder": { - "amount": "22.0", + "amount": "30.0", "unit": "asset" } }, @@ -289,7 +295,7 @@ "IOT" ], "minimalOrder": { - "amount": "10.0", + "amount": "16.0", "unit": "asset" } }, @@ -299,7 +305,7 @@ "IOT" ], "minimalOrder": { - "amount": "10.0", + "amount": "16.0", "unit": "asset" } }, @@ -309,7 +315,7 @@ "IOT" ], "minimalOrder": { - "amount": "10.0", + "amount": "16.0", "unit": "asset" } }, @@ -349,7 +355,7 @@ "SAN" ], "minimalOrder": { - "amount": "14.0", + "amount": "16.0", "unit": "asset" } }, @@ -359,7 +365,7 @@ "SAN" ], "minimalOrder": { - "amount": "14.0", + "amount": "16.0", "unit": "asset" } }, @@ -369,7 +375,7 @@ "SAN" ], "minimalOrder": { - "amount": "14.0", + "amount": "16.0", "unit": "asset" } }, @@ -469,7 +475,7 @@ "ETP" ], "minimalOrder": { - "amount": "16.0", + "amount": "10.0", "unit": "asset" } }, @@ -479,7 +485,7 @@ "ETP" ], "minimalOrder": { - "amount": "16.0", + "amount": "10.0", "unit": "asset" } }, @@ -489,7 +495,7 @@ "ETP" ], "minimalOrder": { - "amount": "16.0", + "amount": "10.0", "unit": "asset" } }, @@ -529,7 +535,7 @@ "AVT" ], "minimalOrder": { - "amount": "12.0", + "amount": "16.0", "unit": "asset" } }, @@ -539,7 +545,7 @@ "AVT" ], "minimalOrder": { - "amount": "12.0", + "amount": "16.0", "unit": "asset" } }, @@ -549,7 +555,7 @@ "AVT" ], "minimalOrder": { - "amount": "12.0", + "amount": "16.0", "unit": "asset" } }, @@ -559,7 +565,7 @@ "EDO" ], "minimalOrder": { - "amount": "12.0", + "amount": "16.0", "unit": "asset" } }, @@ -569,7 +575,7 @@ "EDO" ], "minimalOrder": { - "amount": "12.0", + "amount": "16.0", "unit": "asset" } }, @@ -579,7 +585,7 @@ "EDO" ], "minimalOrder": { - "amount": "12.0", + "amount": "16.0", "unit": "asset" } }, @@ -609,7 +615,7 @@ "DAT" ], "minimalOrder": { - "amount": "194.0", + "amount": "256.0", "unit": "asset" } }, @@ -619,7 +625,7 @@ "DAT" ], "minimalOrder": { - "amount": "194.0", + "amount": "256.0", "unit": "asset" } }, @@ -629,7 +635,7 @@ "DAT" ], "minimalOrder": { - "amount": "194.0", + "amount": "256.0", "unit": "asset" } }, @@ -639,7 +645,7 @@ "QSH" ], "minimalOrder": { - "amount": "46.0", + "amount": "68.0", "unit": "asset" } }, @@ -649,7 +655,7 @@ "QSH" ], "minimalOrder": { - "amount": "46.0", + "amount": "68.0", "unit": "asset" } }, @@ -659,7 +665,7 @@ "QSH" ], "minimalOrder": { - "amount": "46.0", + "amount": "68.0", "unit": "asset" } }, @@ -669,7 +675,7 @@ "YYW" ], "minimalOrder": { - "amount": "198.0", + "amount": "374.0", "unit": "asset" } }, @@ -679,7 +685,7 @@ "YYW" ], "minimalOrder": { - "amount": "198.0", + "amount": "374.0", "unit": "asset" } }, @@ -689,7 +695,7 @@ "YYW" ], "minimalOrder": { - "amount": "198.0", + "amount": "374.0", "unit": "asset" } }, @@ -699,7 +705,7 @@ "GNT" ], "minimalOrder": { - "amount": "36.0", + "amount": "54.0", "unit": "asset" } }, @@ -709,7 +715,7 @@ "GNT" ], "minimalOrder": { - "amount": "36.0", + "amount": "54.0", "unit": "asset" } }, @@ -719,7 +725,7 @@ "GNT" ], "minimalOrder": { - "amount": "36.0", + "amount": "54.0", "unit": "asset" } }, @@ -729,7 +735,7 @@ "SNT" ], "minimalOrder": { - "amount": "192.0", + "amount": "216.0", "unit": "asset" } }, @@ -739,7 +745,7 @@ "SNT" ], "minimalOrder": { - "amount": "192.0", + "amount": "216.0", "unit": "asset" } }, @@ -749,7 +755,7 @@ "SNT" ], "minimalOrder": { - "amount": "192.0", + "amount": "216.0", "unit": "asset" } }, @@ -759,7 +765,7 @@ "IOT" ], "minimalOrder": { - "amount": "10.0", + "amount": "16.0", "unit": "asset" } }, @@ -829,7 +835,7 @@ "FUN" ], "minimalOrder": { - "amount": "446.0", + "amount": "554.0", "unit": "asset" } }, @@ -839,7 +845,7 @@ "FUN" ], "minimalOrder": { - "amount": "446.0", + "amount": "554.0", "unit": "asset" } }, @@ -849,7 +855,7 @@ "FUN" ], "minimalOrder": { - "amount": "446.0", + "amount": "554.0", "unit": "asset" } }, @@ -889,7 +895,7 @@ "TNB" ], "minimalOrder": { - "amount": "528.0", + "amount": "652.0", "unit": "asset" } }, @@ -899,7 +905,7 @@ "TNB" ], "minimalOrder": { - "amount": "528.0", + "amount": "652.0", "unit": "asset" } }, @@ -909,7 +915,7 @@ "TNB" ], "minimalOrder": { - "amount": "528.0", + "amount": "652.0", "unit": "asset" } }, @@ -919,7 +925,7 @@ "SPK" ], "minimalOrder": { - "amount": "128.0", + "amount": "184.0", "unit": "asset" } }, @@ -929,7 +935,7 @@ "SPK" ], "minimalOrder": { - "amount": "128.0", + "amount": "184.0", "unit": "asset" } }, @@ -939,7 +945,7 @@ "SPK" ], "minimalOrder": { - "amount": "128.0", + "amount": "184.0", "unit": "asset" } }, @@ -949,7 +955,7 @@ "TRX" ], "minimalOrder": { - "amount": "310.0", + "amount": "410.0", "unit": "asset" } }, @@ -959,7 +965,7 @@ "TRX" ], "minimalOrder": { - "amount": "310.0", + "amount": "410.0", "unit": "asset" } }, @@ -969,7 +975,7 @@ "TRX" ], "minimalOrder": { - "amount": "310.0", + "amount": "410.0", "unit": "asset" } }, @@ -979,7 +985,7 @@ "RCN" ], "minimalOrder": { - "amount": "236.0", + "amount": "322.0", "unit": "asset" } }, @@ -989,7 +995,7 @@ "RCN" ], "minimalOrder": { - "amount": "236.0", + "amount": "322.0", "unit": "asset" } }, @@ -999,7 +1005,7 @@ "RCN" ], "minimalOrder": { - "amount": "236.0", + "amount": "322.0", "unit": "asset" } }, @@ -1009,7 +1015,7 @@ "RLC" ], "minimalOrder": { - "amount": "14.0", + "amount": "28.0", "unit": "asset" } }, @@ -1019,7 +1025,7 @@ "RLC" ], "minimalOrder": { - "amount": "14.0", + "amount": "28.0", "unit": "asset" } }, @@ -1029,7 +1035,7 @@ "RLC" ], "minimalOrder": { - "amount": "14.0", + "amount": "28.0", "unit": "asset" } }, @@ -1039,7 +1045,7 @@ "AID" ], "minimalOrder": { - "amount": "102.0", + "amount": "122.0", "unit": "asset" } }, @@ -1049,7 +1055,7 @@ "AID" ], "minimalOrder": { - "amount": "102.0", + "amount": "122.0", "unit": "asset" } }, @@ -1059,7 +1065,7 @@ "AID" ], "minimalOrder": { - "amount": "102.0", + "amount": "122.0", "unit": "asset" } }, @@ -1069,7 +1075,7 @@ "SNG" ], "minimalOrder": { - "amount": "298.0", + "amount": "452.0", "unit": "asset" } }, @@ -1079,7 +1085,7 @@ "SNG" ], "minimalOrder": { - "amount": "298.0", + "amount": "452.0", "unit": "asset" } }, @@ -1089,7 +1095,7 @@ "SNG" ], "minimalOrder": { - "amount": "298.0", + "amount": "452.0", "unit": "asset" } }, @@ -1259,7 +1265,7 @@ "IOT" ], "minimalOrder": { - "amount": "10.0", + "amount": "16.0", "unit": "asset" } }, @@ -1269,7 +1275,7 @@ "IOT" ], "minimalOrder": { - "amount": "10.0", + "amount": "16.0", "unit": "asset" } }, @@ -1279,7 +1285,7 @@ "IOS" ], "minimalOrder": { - "amount": "484.0", + "amount": "596.0", "unit": "asset" } }, @@ -1289,7 +1295,7 @@ "IOS" ], "minimalOrder": { - "amount": "484.0", + "amount": "596.0", "unit": "asset" } }, @@ -1299,7 +1305,7 @@ "IOS" ], "minimalOrder": { - "amount": "484.0", + "amount": "596.0", "unit": "asset" } }, @@ -1309,7 +1315,7 @@ "AIO" ], "minimalOrder": { - "amount": "12.0", + "amount": "20.0", "unit": "asset" } }, @@ -1319,7 +1325,7 @@ "AIO" ], "minimalOrder": { - "amount": "12.0", + "amount": "20.0", "unit": "asset" } }, @@ -1329,7 +1335,7 @@ "AIO" ], "minimalOrder": { - "amount": "12.0", + "amount": "20.0", "unit": "asset" } }, @@ -1339,7 +1345,7 @@ "REQ" ], "minimalOrder": { - "amount": "156.0", + "amount": "206.0", "unit": "asset" } }, @@ -1349,7 +1355,7 @@ "REQ" ], "minimalOrder": { - "amount": "156.0", + "amount": "206.0", "unit": "asset" } }, @@ -1359,7 +1365,7 @@ "REQ" ], "minimalOrder": { - "amount": "156.0", + "amount": "206.0", "unit": "asset" } }, @@ -1369,7 +1375,7 @@ "RDN" ], "minimalOrder": { - "amount": "14.0", + "amount": "18.0", "unit": "asset" } }, @@ -1379,7 +1385,7 @@ "RDN" ], "minimalOrder": { - "amount": "14.0", + "amount": "18.0", "unit": "asset" } }, @@ -1389,7 +1395,7 @@ "RDN" ], "minimalOrder": { - "amount": "14.0", + "amount": "18.0", "unit": "asset" } }, @@ -1399,7 +1405,7 @@ "LRC" ], "minimalOrder": { - "amount": "38.0", + "amount": "80.0", "unit": "asset" } }, @@ -1409,7 +1415,7 @@ "LRC" ], "minimalOrder": { - "amount": "38.0", + "amount": "80.0", "unit": "asset" } }, @@ -1419,7 +1425,7 @@ "LRC" ], "minimalOrder": { - "amount": "38.0", + "amount": "80.0", "unit": "asset" } }, @@ -1489,7 +1495,7 @@ "CFI" ], "minimalOrder": { - "amount": "290.0", + "amount": "368.0", "unit": "asset" } }, @@ -1499,7 +1505,7 @@ "CFI" ], "minimalOrder": { - "amount": "290.0", + "amount": "368.0", "unit": "asset" } }, @@ -1509,7 +1515,7 @@ "CFI" ], "minimalOrder": { - "amount": "290.0", + "amount": "368.0", "unit": "asset" } }, @@ -1519,7 +1525,7 @@ "AGI" ], "minimalOrder": { - "amount": "100.0", + "amount": "158.0", "unit": "asset" } }, @@ -1529,7 +1535,7 @@ "AGI" ], "minimalOrder": { - "amount": "100.0", + "amount": "158.0", "unit": "asset" } }, @@ -1539,7 +1545,7 @@ "AGI" ], "minimalOrder": { - "amount": "100.0", + "amount": "158.0", "unit": "asset" } }, @@ -1549,7 +1555,7 @@ "BFT" ], "minimalOrder": { - "amount": "138.0", + "amount": "180.0", "unit": "asset" } }, @@ -1559,7 +1565,7 @@ "BFT" ], "minimalOrder": { - "amount": "138.0", + "amount": "180.0", "unit": "asset" } }, @@ -1569,7 +1575,7 @@ "BFT" ], "minimalOrder": { - "amount": "138.0", + "amount": "180.0", "unit": "asset" } }, @@ -1579,7 +1585,7 @@ "MTN" ], "minimalOrder": { - "amount": "178.0", + "amount": "334.0", "unit": "asset" } }, @@ -1589,7 +1595,7 @@ "MTN" ], "minimalOrder": { - "amount": "178.0", + "amount": "334.0", "unit": "asset" } }, @@ -1599,7 +1605,7 @@ "MTN" ], "minimalOrder": { - "amount": "178.0", + "amount": "334.0", "unit": "asset" } }, @@ -1609,7 +1615,7 @@ "ODE" ], "minimalOrder": { - "amount": "66.0", + "amount": "50.0", "unit": "asset" } }, @@ -1619,7 +1625,7 @@ "ODE" ], "minimalOrder": { - "amount": "66.0", + "amount": "50.0", "unit": "asset" } }, @@ -1629,7 +1635,7 @@ "ODE" ], "minimalOrder": { - "amount": "66.0", + "amount": "50.0", "unit": "asset" } }, @@ -1639,7 +1645,7 @@ "ANT" ], "minimalOrder": { - "amount": "6.0", + "amount": "8.0", "unit": "asset" } }, @@ -1649,7 +1655,7 @@ "ANT" ], "minimalOrder": { - "amount": "6.0", + "amount": "8.0", "unit": "asset" } }, @@ -1659,7 +1665,7 @@ "ANT" ], "minimalOrder": { - "amount": "6.0", + "amount": "8.0", "unit": "asset" } }, @@ -1669,7 +1675,7 @@ "DTH" ], "minimalOrder": { - "amount": "302.0", + "amount": "304.0", "unit": "asset" } }, @@ -1679,7 +1685,7 @@ "DTH" ], "minimalOrder": { - "amount": "302.0", + "amount": "304.0", "unit": "asset" } }, @@ -1689,7 +1695,7 @@ "DTH" ], "minimalOrder": { - "amount": "302.0", + "amount": "304.0", "unit": "asset" } }, @@ -1819,7 +1825,7 @@ "XVG" ], "minimalOrder": { - "amount": "470.0", + "amount": "664.0", "unit": "asset" } }, @@ -1829,7 +1835,7 @@ "XVG" ], "minimalOrder": { - "amount": "470.0", + "amount": "664.0", "unit": "asset" } }, @@ -1839,7 +1845,7 @@ "XVG" ], "minimalOrder": { - "amount": "470.0", + "amount": "664.0", "unit": "asset" } }, @@ -1849,7 +1855,7 @@ "XVG" ], "minimalOrder": { - "amount": "470.0", + "amount": "664.0", "unit": "asset" } }, @@ -1859,7 +1865,7 @@ "XVG" ], "minimalOrder": { - "amount": "470.0", + "amount": "664.0", "unit": "asset" } }, @@ -1869,7 +1875,7 @@ "XVG" ], "minimalOrder": { - "amount": "470.0", + "amount": "664.0", "unit": "asset" } }, @@ -1879,7 +1885,7 @@ "BCI" ], "minimalOrder": { - "amount": "6.0", + "amount": "8.0", "unit": "asset" } }, @@ -1889,7 +1895,7 @@ "BCI" ], "minimalOrder": { - "amount": "6.0", + "amount": "8.0", "unit": "asset" } }, @@ -1929,7 +1935,7 @@ "VEN" ], "minimalOrder": { - "amount": "4.0", + "amount": "10.0", "unit": "asset" } }, @@ -1939,7 +1945,7 @@ "VEN" ], "minimalOrder": { - "amount": "4.0", + "amount": "10.0", "unit": "asset" } }, @@ -1949,7 +1955,7 @@ "VEN" ], "minimalOrder": { - "amount": "4.0", + "amount": "10.0", "unit": "asset" } }, @@ -1959,7 +1965,7 @@ "KNC" ], "minimalOrder": { - "amount": "14.0", + "amount": "18.0", "unit": "asset" } }, @@ -1969,7 +1975,7 @@ "KNC" ], "minimalOrder": { - "amount": "14.0", + "amount": "18.0", "unit": "asset" } }, @@ -1979,7 +1985,7 @@ "KNC" ], "minimalOrder": { - "amount": "14.0", + "amount": "18.0", "unit": "asset" } }, @@ -1989,7 +1995,7 @@ "POA" ], "minimalOrder": { - "amount": "62.0", + "amount": "102.0", "unit": "asset" } }, @@ -1999,7 +2005,7 @@ "POA" ], "minimalOrder": { - "amount": "62.0", + "amount": "102.0", "unit": "asset" } }, @@ -2009,7 +2015,7 @@ "POA" ], "minimalOrder": { - "amount": "62.0", + "amount": "102.0", "unit": "asset" } }, @@ -2019,7 +2025,7 @@ "LYM" ], "minimalOrder": { - "amount": "410.0", + "amount": "584.0", "unit": "asset" } }, @@ -2029,7 +2035,7 @@ "LYM" ], "minimalOrder": { - "amount": "410.0", + "amount": "584.0", "unit": "asset" } }, @@ -2039,7 +2045,7 @@ "LYM" ], "minimalOrder": { - "amount": "410.0", + "amount": "584.0", "unit": "asset" } }, @@ -2049,7 +2055,7 @@ "UTK" ], "minimalOrder": { - "amount": "162.0", + "amount": "218.0", "unit": "asset" } }, @@ -2059,7 +2065,7 @@ "UTK" ], "minimalOrder": { - "amount": "162.0", + "amount": "218.0", "unit": "asset" } }, @@ -2069,7 +2075,7 @@ "UTK" ], "minimalOrder": { - "amount": "162.0", + "amount": "218.0", "unit": "asset" } }, @@ -2079,7 +2085,7 @@ "VEE" ], "minimalOrder": { - "amount": "606.0", + "amount": "774.0", "unit": "asset" } }, @@ -2089,7 +2095,7 @@ "VEE" ], "minimalOrder": { - "amount": "606.0", + "amount": "774.0", "unit": "asset" } }, @@ -2099,7 +2105,7 @@ "VEE" ], "minimalOrder": { - "amount": "606.0", + "amount": "774.0", "unit": "asset" } }, @@ -2109,7 +2115,7 @@ "DAD" ], "minimalOrder": { - "amount": "82.0", + "amount": "140.0", "unit": "asset" } }, @@ -2119,7 +2125,7 @@ "DAD" ], "minimalOrder": { - "amount": "82.0", + "amount": "140.0", "unit": "asset" } }, @@ -2129,7 +2135,7 @@ "DAD" ], "minimalOrder": { - "amount": "82.0", + "amount": "140.0", "unit": "asset" } }, @@ -2169,7 +2175,7 @@ "AUC" ], "minimalOrder": { - "amount": "68.0", + "amount": "102.0", "unit": "asset" } }, @@ -2179,7 +2185,7 @@ "AUC" ], "minimalOrder": { - "amount": "68.0", + "amount": "102.0", "unit": "asset" } }, @@ -2189,7 +2195,7 @@ "AUC" ], "minimalOrder": { - "amount": "68.0", + "amount": "102.0", "unit": "asset" } }, @@ -2199,7 +2205,7 @@ "POY" ], "minimalOrder": { - "amount": "36.0", + "amount": "44.0", "unit": "asset" } }, @@ -2209,7 +2215,7 @@ "POY" ], "minimalOrder": { - "amount": "36.0", + "amount": "44.0", "unit": "asset" } }, @@ -2219,7 +2225,7 @@ "POY" ], "minimalOrder": { - "amount": "36.0", + "amount": "44.0", "unit": "asset" } }, @@ -2229,7 +2235,7 @@ "FSN" ], "minimalOrder": { - "amount": "4.0", + "amount": "8.0", "unit": "asset" } }, @@ -2239,7 +2245,7 @@ "FSN" ], "minimalOrder": { - "amount": "4.0", + "amount": "8.0", "unit": "asset" } }, @@ -2249,7 +2255,7 @@ "FSN" ], "minimalOrder": { - "amount": "4.0", + "amount": "8.0", "unit": "asset" } }, @@ -2259,7 +2265,7 @@ "CBT" ], "minimalOrder": { - "amount": "496.0", + "amount": "562.0", "unit": "asset" } }, @@ -2269,7 +2275,7 @@ "CBT" ], "minimalOrder": { - "amount": "496.0", + "amount": "562.0", "unit": "asset" } }, @@ -2279,7 +2285,7 @@ "CBT" ], "minimalOrder": { - "amount": "496.0", + "amount": "562.0", "unit": "asset" } }, @@ -2289,7 +2295,7 @@ "ZCN" ], "minimalOrder": { - "amount": "22.0", + "amount": "52.0", "unit": "asset" } }, @@ -2299,7 +2305,7 @@ "ZCN" ], "minimalOrder": { - "amount": "22.0", + "amount": "52.0", "unit": "asset" } }, @@ -2309,7 +2315,7 @@ "ZCN" ], "minimalOrder": { - "amount": "22.0", + "amount": "52.0", "unit": "asset" } }, @@ -2319,7 +2325,7 @@ "SEN" ], "minimalOrder": { - "amount": "596.0", + "amount": "906.0", "unit": "asset" } }, @@ -2329,7 +2335,7 @@ "SEN" ], "minimalOrder": { - "amount": "596.0", + "amount": "906.0", "unit": "asset" } }, @@ -2339,7 +2345,7 @@ "SEN" ], "minimalOrder": { - "amount": "596.0", + "amount": "906.0", "unit": "asset" } }, @@ -2349,7 +2355,7 @@ "NCA" ], "minimalOrder": { - "amount": "826.0", + "amount": "1352.0", "unit": "asset" } }, @@ -2359,7 +2365,7 @@ "NCA" ], "minimalOrder": { - "amount": "826.0", + "amount": "1352.0", "unit": "asset" } }, @@ -2369,7 +2375,7 @@ "NCA" ], "minimalOrder": { - "amount": "826.0", + "amount": "1352.0", "unit": "asset" } }, @@ -2379,7 +2385,7 @@ "CND" ], "minimalOrder": { - "amount": "358.0", + "amount": "498.0", "unit": "asset" } }, @@ -2389,7 +2395,7 @@ "CND" ], "minimalOrder": { - "amount": "358.0", + "amount": "498.0", "unit": "asset" } }, @@ -2399,7 +2405,7 @@ "CND" ], "minimalOrder": { - "amount": "358.0", + "amount": "498.0", "unit": "asset" } }, @@ -2409,7 +2415,7 @@ "CTX" ], "minimalOrder": { - "amount": "18.0", + "amount": "30.0", "unit": "asset" } }, @@ -2419,7 +2425,7 @@ "CTX" ], "minimalOrder": { - "amount": "18.0", + "amount": "30.0", "unit": "asset" } }, @@ -2429,7 +2435,7 @@ "CTX" ], "minimalOrder": { - "amount": "18.0", + "amount": "30.0", "unit": "asset" } }, @@ -2439,7 +2445,7 @@ "PAI" ], "minimalOrder": { - "amount": "10.0", + "amount": "20.0", "unit": "asset" } }, @@ -2449,7 +2455,7 @@ "PAI" ], "minimalOrder": { - "amount": "10.0", + "amount": "20.0", "unit": "asset" } }, @@ -2459,7 +2465,7 @@ "SEE" ], "minimalOrder": { - "amount": "1654.0", + "amount": "3262.0", "unit": "asset" } }, @@ -2469,7 +2475,7 @@ "SEE" ], "minimalOrder": { - "amount": "1654.0", + "amount": "3262.0", "unit": "asset" } }, @@ -2479,7 +2485,7 @@ "SEE" ], "minimalOrder": { - "amount": "1654.0", + "amount": "3262.0", "unit": "asset" } }, @@ -2489,7 +2495,7 @@ "ESS" ], "minimalOrder": { - "amount": "452.0", + "amount": "798.0", "unit": "asset" } }, @@ -2499,7 +2505,7 @@ "ESS" ], "minimalOrder": { - "amount": "452.0", + "amount": "798.0", "unit": "asset" } }, @@ -2509,7 +2515,7 @@ "ESS" ], "minimalOrder": { - "amount": "452.0", + "amount": "798.0", "unit": "asset" } }, @@ -2519,7 +2525,7 @@ "ATM" ], "minimalOrder": { - "amount": "316.0", + "amount": "516.0", "unit": "asset" } }, @@ -2529,7 +2535,7 @@ "ATM" ], "minimalOrder": { - "amount": "316.0", + "amount": "516.0", "unit": "asset" } }, @@ -2539,7 +2545,7 @@ "ATM" ], "minimalOrder": { - "amount": "316.0", + "amount": "516.0", "unit": "asset" } }, @@ -2549,7 +2555,7 @@ "HOT" ], "minimalOrder": { - "amount": "330.0", + "amount": "562.0", "unit": "asset" } }, @@ -2559,7 +2565,7 @@ "HOT" ], "minimalOrder": { - "amount": "330.0", + "amount": "562.0", "unit": "asset" } }, @@ -2569,7 +2575,7 @@ "HOT" ], "minimalOrder": { - "amount": "330.0", + "amount": "562.0", "unit": "asset" } }, @@ -2579,7 +2585,7 @@ "DTA" ], "minimalOrder": { - "amount": "1560.0", + "amount": "3480.0", "unit": "asset" } }, @@ -2589,7 +2595,7 @@ "DTA" ], "minimalOrder": { - "amount": "1560.0", + "amount": "3480.0", "unit": "asset" } }, @@ -2599,7 +2605,7 @@ "DTA" ], "minimalOrder": { - "amount": "1560.0", + "amount": "3480.0", "unit": "asset" } }, @@ -2609,7 +2615,7 @@ "IQX" ], "minimalOrder": { - "amount": "190.0", + "amount": "578.0", "unit": "asset" } }, @@ -2619,7 +2625,7 @@ "IQX" ], "minimalOrder": { - "amount": "190.0", + "amount": "578.0", "unit": "asset" } }, @@ -2629,7 +2635,157 @@ "IQX" ], "minimalOrder": { - "amount": "190.0", + "amount": "578.0", + "unit": "asset" + } + }, + { + "pair": [ + "USD", + "WPR" + ], + "minimalOrder": { + "amount": "370.0", + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "WPR" + ], + "minimalOrder": { + "amount": "370.0", + "unit": "asset" + } + }, + { + "pair": [ + "ETH", + "WPR" + ], + "minimalOrder": { + "amount": "370.0", + "unit": "asset" + } + }, + { + "pair": [ + "USD", + "ZIL" + ], + "minimalOrder": { + "amount": "248.0", + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "ZIL" + ], + "minimalOrder": { + "amount": "248.0", + "unit": "asset" + } + }, + { + "pair": [ + "ETH", + "ZIL" + ], + "minimalOrder": { + "amount": "248.0", + "unit": "asset" + } + }, + { + "pair": [ + "USD", + "BNT" + ], + "minimalOrder": { + "amount": "6.0", + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "BNT" + ], + "minimalOrder": { + "amount": "6.0", + "unit": "asset" + } + }, + { + "pair": [ + "ETH", + "BNT" + ], + "minimalOrder": { + "amount": "6.0", + "unit": "asset" + } + }, + { + "pair": [ + "USD", + "ABS" + ], + "minimalOrder": { + "amount": "278.0", + "unit": "asset" + } + }, + { + "pair": [ + "ETH", + "ABS" + ], + "minimalOrder": { + "amount": "278.0", + "unit": "asset" + } + }, + { + "pair": [ + "USD", + "XRA" + ], + "minimalOrder": { + "amount": "62.0", + "unit": "asset" + } + }, + { + "pair": [ + "ETH", + "XRA" + ], + "minimalOrder": { + "amount": "62.0", + "unit": "asset" + } + }, + { + "pair": [ + "USD", + "MAN" + ], + "minimalOrder": { + "amount": "30.0", + "unit": "asset" + } + }, + { + "pair": [ + "ETH", + "MAN" + ], + "minimalOrder": { + "amount": "30.0", "unit": "asset" } } diff --git a/exchange/wrappers/coinfalcon-markets.json b/exchange/wrappers/coinfalcon-markets.json index f0da4320f..b2779666f 100644 --- a/exchange/wrappers/coinfalcon-markets.json +++ b/exchange/wrappers/coinfalcon-markets.json @@ -6,34 +6,16 @@ "BTC", "NANO", "BCH", - "TAU", - "CRED", "KIN", - "SPHTX", "TRX", - "VET", "CRPT", - "GNT", - "DCN", - "REBL", - "VIU", - "EBTC", - "TRAC", - "CHSB", "GRLC", "ECA", "XRP", - "WPR", - "XBY", - "WAX", - "DADI", - "REF", "ADA", - "LDN", "DOGE", "STQ", "DASC", - "LET", "EOS", "DTX" ], @@ -88,17 +70,6 @@ "order": 0 } }, - { - "pair": [ - "ETH", - "NANO" - ], - "minimalOrder": { - "amount": 1e-8, - "price": 1e-8, - "order": 0 - } - }, { "pair": [ "BTC", @@ -121,50 +92,6 @@ "order": 0 } }, - { - "pair": [ - "ETH", - "TAU" - ], - "minimalOrder": { - "amount": 1e-8, - "price": 1e-8, - "order": 0 - } - }, - { - "pair": [ - "BTC", - "TAU" - ], - "minimalOrder": { - "amount": 1e-8, - "price": 1e-8, - "order": 0 - } - }, - { - "pair": [ - "BTC", - "CRED" - ], - "minimalOrder": { - "amount": 1e-8, - "price": 0.000001, - "order": 0 - } - }, - { - "pair": [ - "ETH", - "CRED" - ], - "minimalOrder": { - "amount": 1e-8, - "price": 0.000001, - "order": 0 - } - }, { "pair": [ "BTC", @@ -187,28 +114,6 @@ "order": 0 } }, - { - "pair": [ - "BTC", - "SPHTX" - ], - "minimalOrder": { - "amount": 1e-8, - "price": 1e-8, - "order": 0 - } - }, - { - "pair": [ - "ETH", - "SPHTX" - ], - "minimalOrder": { - "amount": 1e-8, - "price": 1e-8, - "order": 0 - } - }, { "pair": [ "BTC", @@ -220,39 +125,6 @@ "order": 0 } }, - { - "pair": [ - "ETH", - "TRX" - ], - "minimalOrder": { - "amount": 1e-8, - "price": 1e-8, - "order": 0 - } - }, - { - "pair": [ - "BTC", - "VET" - ], - "minimalOrder": { - "amount": 1e-8, - "price": 1e-8, - "order": 0 - } - }, - { - "pair": [ - "ETH", - "VET" - ], - "minimalOrder": { - "amount": 1e-8, - "price": 1e-8, - "order": 0 - } - }, { "pair": [ "BTC", @@ -264,160 +136,6 @@ "order": 0 } }, - { - "pair": [ - "ETH", - "CRPT" - ], - "minimalOrder": { - "amount": 1e-8, - "price": 1e-8, - "order": 0 - } - }, - { - "pair": [ - "BTC", - "GNT" - ], - "minimalOrder": { - "amount": 1e-8, - "price": 1e-8, - "order": 0 - } - }, - { - "pair": [ - "ETH", - "GNT" - ], - "minimalOrder": { - "amount": 1e-8, - "price": 1e-8, - "order": 0 - } - }, - { - "pair": [ - "BTC", - "DCN" - ], - "minimalOrder": { - "amount": 1e-8, - "price": 1e-8, - "order": 0 - } - }, - { - "pair": [ - "ETH", - "DCN" - ], - "minimalOrder": { - "amount": 1e-8, - "price": 1e-8, - "order": 0 - } - }, - { - "pair": [ - "BTC", - "REBL" - ], - "minimalOrder": { - "amount": 1e-8, - "price": 1e-8, - "order": 0 - } - }, - { - "pair": [ - "ETH", - "REBL" - ], - "minimalOrder": { - "amount": 1e-8, - "price": 1e-8, - "order": 0 - } - }, - { - "pair": [ - "BTC", - "VIU" - ], - "minimalOrder": { - "amount": 1e-8, - "price": 1e-8, - "order": 0 - } - }, - { - "pair": [ - "BTC", - "EBTC" - ], - "minimalOrder": { - "amount": 1e-8, - "price": 1e-8, - "order": 0 - } - }, - { - "pair": [ - "ETH", - "EBTC" - ], - "minimalOrder": { - "amount": 1e-8, - "price": 1e-8, - "order": 0 - } - }, - { - "pair": [ - "BTC", - "TRAC" - ], - "minimalOrder": { - "amount": 1e-8, - "price": 1e-8, - "order": 0 - } - }, - { - "pair": [ - "ETH", - "TRAC" - ], - "minimalOrder": { - "amount": 1e-8, - "price": 1e-8, - "order": 0 - } - }, - { - "pair": [ - "BTC", - "CHSB" - ], - "minimalOrder": { - "amount": 1e-8, - "price": 1e-8, - "order": 0 - } - }, - { - "pair": [ - "ETH", - "CHSB" - ], - "minimalOrder": { - "amount": 1e-8, - "price": 1e-8, - "order": 0 - } - }, { "pair": [ "BTC", @@ -429,17 +147,6 @@ "order": 0 } }, - { - "pair": [ - "ETH", - "GRLC" - ], - "minimalOrder": { - "amount": 1e-8, - "price": 1e-8, - "order": 0 - } - }, { "pair": [ "BTC", @@ -473,127 +180,6 @@ "order": 0 } }, - { - "pair": [ - "ETH", - "XRP" - ], - "minimalOrder": { - "amount": 1e-8, - "price": 1e-8, - "order": 0 - } - }, - { - "pair": [ - "BTC", - "WPR" - ], - "minimalOrder": { - "amount": 0.000001, - "price": 1e-8, - "order": 0 - } - }, - { - "pair": [ - "ETH", - "WPR" - ], - "minimalOrder": { - "amount": 0.000001, - "price": 1e-8, - "order": 0 - } - }, - { - "pair": [ - "BTC", - "XBY" - ], - "minimalOrder": { - "amount": 0.01, - "price": 1e-8, - "order": 0 - } - }, - { - "pair": [ - "ETH", - "XBY" - ], - "minimalOrder": { - "amount": 0.01, - "price": 1e-8, - "order": 0 - } - }, - { - "pair": [ - "BTC", - "WAX" - ], - "minimalOrder": { - "amount": 0.01, - "price": 1e-8, - "order": 0 - } - }, - { - "pair": [ - "ETH", - "WAX" - ], - "minimalOrder": { - "amount": 0.01, - "price": 1e-8, - "order": 0 - } - }, - { - "pair": [ - "BTC", - "DADI" - ], - "minimalOrder": { - "amount": 0.000001, - "price": 1e-8, - "order": 0 - } - }, - { - "pair": [ - "ETH", - "DADI" - ], - "minimalOrder": { - "amount": 0.000001, - "price": 1e-8, - "order": 0 - } - }, - { - "pair": [ - "BTC", - "REF" - ], - "minimalOrder": { - "amount": 0.000001, - "price": 1e-8, - "order": 0 - } - }, - { - "pair": [ - "ETH", - "REF" - ], - "minimalOrder": { - "amount": 0.000001, - "price": 1e-8, - "order": 0 - } - }, { "pair": [ "BTC", @@ -616,28 +202,6 @@ "order": 0 } }, - { - "pair": [ - "BTC", - "LDN" - ], - "minimalOrder": { - "amount": 0.00001, - "price": 1e-8, - "order": 0 - } - }, - { - "pair": [ - "ETH", - "LDN" - ], - "minimalOrder": { - "amount": 0.00001, - "price": 1e-8, - "order": 0 - } - }, { "pair": [ "BTC", @@ -649,39 +213,6 @@ "order": 0 } }, - { - "pair": [ - "ETH", - "IOT" - ], - "minimalOrder": { - "amount": 0.00001, - "price": 1e-8, - "order": 0 - } - }, - { - "pair": [ - "ETH", - "DOGE" - ], - "minimalOrder": { - "amount": 0.00001, - "price": 1e-8, - "order": 0 - } - }, - { - "pair": [ - "ETH", - "STQ" - ], - "minimalOrder": { - "amount": 0.01, - "price": 1e-8, - "order": 0 - } - }, { "pair": [ "BTC", @@ -759,17 +290,6 @@ "order": 0 } }, - { - "pair": [ - "USDT", - "LET" - ], - "minimalOrder": { - "amount": 0.01, - "price": 0.0001, - "order": 0 - } - }, { "pair": [ "USDT", From ef7b94e224230c66abbb7d0c66ded9fa0df543a8 Mon Sep 17 00:00:00 2001 From: Wei Ken Date: Fri, 10 Aug 2018 21:47:03 +0800 Subject: [PATCH 12/29] Update Luno (#2417) * v0.6 support for bitx (aka Luno) * v0.6 support for bitx (aka Luno) * remove log * move dependency to gb * rename bitx to luno * rename bitx to luno * temp fix simultaneous advice error * - re-add logging - Retry insufficient balance due to server update delay - Enhanced getPorfolio - getOrder now return fees * update * update * update * revert 4b5d09f --- exchange/wrappers/luno.js | 207 ++++++++++++++++++++---------- strategies/DEBUG_toggle-advice.js | 4 +- 2 files changed, 140 insertions(+), 71 deletions(-) diff --git a/exchange/wrappers/luno.js b/exchange/wrappers/luno.js index b21e12d57..c3f697f94 100644 --- a/exchange/wrappers/luno.js +++ b/exchange/wrappers/luno.js @@ -1,12 +1,13 @@ const Luno = require("bitx"); const _ = require('lodash'); const moment = require('moment'); - +const node_util = require('util'); const Errors = require('../exchangeErrors'); const retry = require('../exchangeUtils').retry; - const name = 'Luno'; +var tradeAttempt = 0; + const Trader = function(config) { if (_.isObject(config)) { this.key = config.key; @@ -15,13 +16,29 @@ const Trader = function(config) { this.asset = config.asset; this.pair = config.asset + config.currency; } - this.luno = new Luno(this.key, this.secret, { pair: this.pair }); + this.luno = new Luno(this.key, this.secret, { + pair: this.pair + }); this.market = _.find(Trader.getCapabilities().markets, (market) => { return market.pair[0] === this.currency && market.pair[1] === this.asset; }); - this.interval = 3000; + this.interval = 2000; +} + +const log = function() { + const message = node_util.format.apply(null, _.toArray(arguments)); + console.log(moment().format('YYYY-MM-DD HH:mm:ss') + ' (DEBUG): ' + message); } +Trader.prototype.inspect = function(obj) { + return node_util.inspect(obj, { + showhidden: false, + depth: null, + breakLength: Infinity, + colors: true + }); +}; + const setPrecision = (amount, digits) => { let precision = 100000000; if (Number.isInteger(digits)) precision = Math.pow(10, digits); @@ -38,7 +55,6 @@ const round = (value, decimals = 14) => { const includes = (str, list) => { if (!_.isString(str)) return false; - let result = _.some(list, item => str.includes(item)); return result; } @@ -51,20 +67,45 @@ const recoverableErrors = [ 'NOTFOUND' ]; + const processResponse = function(funcName, callback) { return (error, body) => { + /* BitX Error Codes + Error: BitX error 400: invalid id + Error: BitX error 401: "error":"API key not found","error_code":"ErrAPIKeyNotFound","error_action": + Error: BitX error 429: -- API limit + Error: BitX error 500: Something went wrong, we're looking into it. + */ + if (!error && !body) { - error = new Error('Empty response'); + error = new Error('received empty response'); } if (error) { + log(name, funcName + '() received error -->', error.message); + if (includes(error.message, recoverableErrors)) { error.notFatal = true; } if (includes(error.message, ['error 429'])) { error.notFatal = true; - error.backoffDelay = 10000; + error.backoffDelay = 3000; + } + + if (includes(error.message, ['error 500'])) { + error.notFatal = true; + error.backoffDelay = 15000; + } + + if (includes(error.message, ['Insufficient balance'])) { + error.notFatal = true; + error.backoffDelay = 2000; + tradeAttempt++; + if (tradeAttempt >= 3) { + error.notFatal = false; + log(name, funcName + '() giving up after 3 failed attempts.'); + } } return callback(error, undefined); @@ -77,9 +118,11 @@ const processResponse = function(funcName, callback) { //------- Gekko Functions ---------// Trader.prototype.getTicker = function(callback) { + // log(name, 'getTicker()'); + const process = (err, data) => { if (err) { - console.log(name, 'Error: --> ', err); + log(name, 'Error: -->', err); return callback(err); } const ticker = { @@ -87,6 +130,7 @@ Trader.prototype.getTicker = function(callback) { bid: data.bid, spread: round(data.ask - data.bid) }; + log(name, 'getTicker() -->', this.inspect(ticker)); callback(undefined, ticker); }; @@ -95,58 +139,61 @@ Trader.prototype.getTicker = function(callback) { } Trader.prototype.getFee = function(callback) { - // const process = (err, data) => { - // if (err) { - // console.log(name, 'Error: --> ', err); - // return callback(err); - // } - // callback(undefined, data.taker_fee / 100); - // }; - // const handler = cb => this.luno.getFee(processResponse('getFee', cb)); - // retry(null, handler, process); + // log(name, 'getFee()'); if (this.pair === 'ETHXBT') - callback(undefined, 0.000025); + return callback(undefined, 0.000025); else if (this.pair === 'XBTIDR') - callback(undefined, 0.00002); + return callback(undefined, 0.00002); else - callback(undefined, 0.0001); + return callback(undefined, 0.0001); + + /* + const process = (err, data) => { + if (err) { + // log(name, 'Error: -->', err); + return callback(err); + } + // log(name, 'getFee() --> fee:', data.taker_fee / 100); + callback(undefined, data.taker_fee / 100); + }; + const handler = cb => this.luno.getFee(processResponse('getFee', cb)); + retry(null, handler, process); + */ } Trader.prototype.getPortfolio = function(callback) { + // log(name, 'getPortfolio()'); + const process = (err, data) => { if (err) { - console.log(name, 'Error: --> ', err); + log(name, 'Error: -->', err); return callback(err); } + const assetProfile = _.find(data.balance, a => a.asset === this.asset); + const currencyProfile = _.find(data.balance, a => a.asset === this.currency); + let assetAmount = round(assetProfile.balance - assetProfile.reserved); + let currencyAmount = round(currencyProfile.balance - currencyProfile.reserved); - let assetAmount = 0, - currencyAmount = 0, - assetHold = 0, - currencyHold = 0; - - _.forEach(data.balance, (t) => { - if (this.asset === t.asset) { - assetAmount = +t.balance; - assetHold = +t.reserved; - } else if (this.currency === t.asset) { - currencyAmount = +t.balance; - currencyHold = +t.reserved; - } - }); + if (!_.isNumber(assetAmount) || _.isNaN(assetAmount)) { + assetAmount = 0; + } - if (!_.isNumber(assetAmount) || _.isNaN(assetAmount) || - !_.isNumber(currencyAmount) || _.isNaN(currencyAmount) - ) { - return console.log(name, 'account balance error: Gekko is unable to trade with ', this.currency.toUpperCase(), ':', currencyAmount, ' or ', this.asset.toUpperCase(), ':', assetAmount); + if (!_.isNumber(currencyAmount) || _.isNaN(currencyAmount)) { + currencyAmount = 0; } - const portfolio = [ - { name: this.asset.toUpperCase(), amount: round(assetAmount - assetHold) }, - { name: this.currency.toUpperCase(), amount: round(currencyAmount - currencyHold) } + const portfolio = [{ + name: this.asset.toUpperCase(), + amount: assetAmount + }, + { + name: this.currency.toUpperCase(), + amount: currencyAmount + } ]; - - callback(err, portfolio); + log(name, 'getPortfolio() --> ' + this.inspect(portfolio)); + callback(undefined, portfolio); }; const handler = cb => this.luno.getBalance(processResponse('getPortfolio', cb)); @@ -154,14 +201,18 @@ Trader.prototype.getPortfolio = function(callback) { } Trader.prototype.buy = function(amount, price, callback) { + log(name, 'buy() amount:', amount, 'price:', price); + + tradeAttempt = 0; amount = round(amount); price = round(price); const process = (err, data) => { if (err) { - console.log(name, 'unable to buy:', err.message); + log(name, 'unable to buy:', err.message); return callback(err); } - callback(err, data.order_id); + log(name, 'buy() order id: -->', data.order_id); + callback(undefined, data.order_id); }; const handler = cb => this.luno.postBuyOrder(amount, price, processResponse('buy', cb)); @@ -169,14 +220,18 @@ Trader.prototype.buy = function(amount, price, callback) { } Trader.prototype.sell = function(amount, price, callback) { + log(name, 'sell() amount:', amount, 'price:', price); + + tradeAttempt = 0; amount = round(amount); price = round(price); const process = (err, data) => { if (err) { - console.log(name, 'unable to sell:', err.message); + log(name, 'unable to sell:', err.message); return callback(err); } - callback(err, data.order_id); + log(name, 'sell() order id: -->', data.order_id); + callback(undefined, data.order_id); }; const handler = cb => this.luno.postSellOrder(amount, price, processResponse('sell', cb)); @@ -193,25 +248,38 @@ Trader.prototype.roundPrice = function(price) { } Trader.prototype.getOrder = function(order, callback) { + // log(name, 'getOrder() order id:', order); + if (!order) { return callback('invalid order_id', false); } const process = (err, data) => { if (err) { - console.log(name, 'Error: --> ', err); + log(name, 'Error: -->', err); return callback(err); } - let price = 0; - let amount = 0; + const price = parseFloat(data.limit_price); + const amount = parseFloat(data.base); let date = moment(); - price = parseFloat(data.limit_price); - amount = parseFloat(data.base); + const fees = { + [this.asset]: +data.fee_base, + [this.currency]: +data.fee_counter + }; + const feePercent = round(data.fee_base / data.base * 100, 2); if (data.state === 'PENDING') { date = moment(data.creation_timestamp); } else { date = moment(data.completed_timestamp); } - callback(err, { price, amount, date }); + const result = { + price, + amount, + date, + fees, + feePercent + }; + log(name, 'getOrder() -->', this.inspect(result)); + callback(undefined, result); }; const handler = cb => this.luno.getOrder(order, processResponse('getOrder', cb)); @@ -219,22 +287,23 @@ Trader.prototype.getOrder = function(order, callback) { } Trader.prototype.checkOrder = function(order, callback) { + // log(name, 'checkOrder() order id:', order); + if (!order) { return callback('invalid order_id'); } const process = (err, data) => { if (err) { - console.log(name, 'Error: --> ', err); + log(name, 'Error: -->', err); return callback(err); } - const result = { open: data.state === 'PENDING', executed: data.limit_volume === data.base, filledAmount: +data.base, remaining: round(+data.limit_volume - +data.base) } - + log(name, 'checkOrder()', order, 'result:', this.inspect(result)); callback(undefined, result); }; @@ -243,44 +312,43 @@ Trader.prototype.checkOrder = function(order, callback) { } Trader.prototype.cancelOrder = function(order, callback) { + log(name, 'cancelOrder() order id:', order); + if (!order) { return callback('invalid order_id'); } const process = (err, data) => { if (err) { if (_.includes(err.message, 'Cannot stop unknown')) { - console.log(name, 'unable to cancel order:', order, '(' + err.message + ') assuming success...'); - // return callback(undefined, true); + log(name, 'unable to cancel order:', order, '(' + err.message + ') assuming success...'); } else { - console.log(name, 'unable to cancel order:', order, '(' + err.message + ') aborting...'); + log(name, 'unable to cancel order:', order, '(' + err.message + ') aborting...'); return callback(err); } } if (data && !data.success) { - console.log('cancelOrder() --> status:', data.success); + log('cancelOrder() --> status:', data.success); return callback(undefined, false); } this.checkOrder(order, (error, orderStatus) => { if (error) { - console.log(name, 'cancelOrder\'s checkOrder failed. What do i do here?'); + log(name, 'cancelOrder\'s checkOrder failed. What do i do here?'); return callback(error, false); } if (orderStatus.executed) { + log(name, 'cancelOrder() -->', order, 'was fulfilled before cancelOrder was completed.'); return callback(undefined, true); } - - // TODO: Fees? const remaining = { remaining: orderStatus.remaining, filled: orderStatus.filledAmount } - + log(name, 'cancelOrder() --> status: false remaining:', this.inspect(remaining)); return callback(undefined, false, remaining); }); - } const handler = cb => this.luno.stopOrder(order, processResponse('cancelOrder', cb)); @@ -288,9 +356,11 @@ Trader.prototype.cancelOrder = function(order, callback) { } Trader.prototype.getTrades = function(since, callback, descending) { + // log(name, 'getTrades() since:', since); + const process = (err, result) => { if (err) { - console.log(name, 'Error: --> ', err); + log(name, 'Error: -->', err); return callback(err); } let trades = _.map(result.trades, (t) => { @@ -301,7 +371,6 @@ Trader.prototype.getTrades = function(since, callback, descending) { tid: t.timestamp } }); - // Decending by default if (!descending) { trades = trades.reverse() } @@ -332,7 +401,7 @@ Trader.getCapabilities = function() { { pair: ['KES', 'XBT'], minimalOrder: { amount: 0.0005, unit: 'asset' }, precision: 6 }, { pair: ['NGN', 'XBT'], minimalOrder: { amount: 0.0005, unit: 'asset' }, precision: 6 }, { pair: ['ZAR', 'XBT'], minimalOrder: { amount: 0.0005, unit: 'asset' }, precision: 6 }, - ], + ], requires: ['key', 'secret'], providesFullHistory: true, providesHistory: 'date', diff --git a/strategies/DEBUG_toggle-advice.js b/strategies/DEBUG_toggle-advice.js index 1fabd2184..251585a57 100644 --- a/strategies/DEBUG_toggle-advice.js +++ b/strategies/DEBUG_toggle-advice.js @@ -23,7 +23,7 @@ var method = { return; log.info('iteration:', i); - + if(i % settings.each === 0) { log.debug('trigger SHORT'); this.advice('short'); @@ -42,4 +42,4 @@ var method = { } }; -module.exports = method; \ No newline at end of file +module.exports = method; From 09050e11c4d6faf7fdb83b18606bd0dfc2511bf0 Mon Sep 17 00:00:00 2001 From: Mike van Rossum Date: Fri, 10 Aug 2018 22:08:06 +0800 Subject: [PATCH 13/29] v.0.6.5 --- package.json | 2 +- web/vue/dist/app.9aa8dda3.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index c952118ea..73d977471 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "gekko", - "version": "0.6.4", + "version": "0.6.5", "description": "A bitcoin trading bot for auto trading at various exchanges", "keywords": [ "trading", diff --git a/web/vue/dist/app.9aa8dda3.js b/web/vue/dist/app.9aa8dda3.js index 79c40a3d9..6c8aebd79 100644 --- a/web/vue/dist/app.9aa8dda3.js +++ b/web/vue/dist/app.9aa8dda3.js @@ -1,2 +1,2 @@ -(function(t){function e(e){for(var n,s,o=e[0],c=e[1],u=e[2],d=0,f=[];d0?"profit":"loss"}}},s=i,o=(a("tr8z"),a("KHd+")),c=Object(o["a"])(s,n,r,!1,null,null,null);e["a"]=c.exports},"0zrD":function(t,e,a){"use strict";var n=a("jf14"),r=a.n(n);r.a},26:function(t,e,a){t.exports=a("Vtdi")},"2A8w":function(t,e,a){"use strict";var n=a("TDb6"),r=a.n(n);r.a},"2Yda":function(t,e,a){"use strict";var n=function(){var t=this,e=t.$createElement,a=t._self._c||e;return a("div",{staticClass:"contain my2"},[a("h3",[t._v("Start a new gekko")]),a("gekko-config-builder",{on:{config:t.updateConfig}}),a("div",{staticClass:"hr"}),t.config.valid?a("div",{staticClass:"txt--center"},[t.pendingStratrunner?t._e():a("a",{staticClass:"w100--s my1 btn--primary",attrs:{href:"#"},on:{click:function(e){return e.preventDefault(),t.start(e)}}},[t._v("Start")]),t.pendingStratrunner?a("spinner"):t._e()],1):t._e()],1)},r=[],i=(a("Z2Ku"),a("L9s1"),a("dRSK"),a("LvDl")),s=a.n(i),o=a("Kw5r"),c=a("wiDz"),u=function(){var t=this,e=t.$createElement,a=t._self._c||e;return a("div",{staticClass:"grd contain"},[a("div",{staticClass:"grd-row"},[a("div",{staticClass:"grd-row-col-3-6 mx1"},[a("h3",[t._v("Market")]),a("market-picker",{attrs:{"only-tradable":t.isTradebot},on:{market:t.updateMarketConfig}})],1),a("div",{staticClass:"grd-row-col-3-6 mx1"},[a("type-picker",{on:{type:t.updateType}})],1)]),"market watcher"!==t.type?[a("div",{staticClass:"hr"}),a("strat-picker",{staticClass:"contain my2",on:{stratConfig:t.updateStrat}}),"paper trader"===t.type?a("div",{staticClass:"hr"}):t._e(),"paper trader"===t.type?a("paper-trader",{on:{settings:t.updatePaperTrader}}):t._e()]:t._e()],2)},l=[],d=(a("91GP"),a("6BxS")),f=function(){var t=this,e=t.$createElement,a=t._self._c||e;return a("div",[a("h3",[t._v("Type")]),[a("label",{staticClass:"wrapper",attrs:{for:"type"}},[t._v("What do you want to do with gekko?")]),a("form",{staticClass:"radio grd"},t._l(t.types,function(e,n){return a("div",{staticClass:"grd-row m1"},[a("input",{directives:[{name:"model",rawName:"v-model",value:t.selectedTypeIndex,expression:"selectedTypeIndex"}],staticClass:"grd-row-col-1-6",attrs:{type:"radio"},domProps:{value:n,checked:t._q(t.selectedTypeIndex,n)},on:{change:function(e){t.selectedTypeIndex=n}}}),a("label",{staticClass:"grd-row-col-5-6",attrs:{for:n}},[t._v(t._s(e))])])}))]],2)},h=[],m={created:function(){this.emitType()},data:function(){return{types:["paper trader","market watcher","tradebot"],selectedTypeIndex:0}},methods:{emitType:function(){this.$emit("type",this.type)}},watch:{type:function(){this.emitType()}},computed:{type:function(){return this.types[this.selectedTypeIndex]}}},v=m,p=(a("wVPO"),a("KHd+")),g=Object(p["a"])(v,f,h,!1,null,null,null),_=g.exports,k=a("6Wkr"),y=a("rloZ"),w={created:function(){var t=this;Object(c["a"])("configPart/candleWriter",function(e,a){t.candleWriter=toml.parse(a.part)}),Object(c["a"])("configPart/performanceAnalyzer",function(e,a){t.performanceAnalyzer=toml.parse(a.part),t.performanceAnalyzer.enabled=!0})},data:function(){return{market:{},range:{},type:"",strat:{},paperTrader:{},candleWriter:{},performanceAnalyzer:{}}},components:{marketPicker:d["a"],typePicker:_,stratPicker:k["a"],paperTrader:y["a"]},computed:{isTradebot:function(){return"tradebot"===this.type},config:function(){var t={};return Object.assign(t,this.market,this.strat,{paperTrader:this.paperTrader},{candleWriter:this.candleWriter},{type:this.type},{performanceAnalyzer:this.performanceAnalyzer}),this.isTradebot&&(delete t.paperTrader,t.trader={enabled:!0}),t.valid=this.validConfig(t),t}},methods:{validConfig:function(t){if("market watcher"===t.type)return!0;if(!t.tradingAdvisor)return!1;if(s.a.isNaN(t.tradingAdvisor.candleSize))return!1;if(0==t.tradingAdvisor.candleSize)return!1;var e=t.tradingAdvisor.method;return!s.a.isEmpty(t[e])},updateMarketConfig:function(t){this.market=t,this.emitConfig()},updateType:function(t){this.type=t,this.emitConfig()},updateStrat:function(t){this.strat=t,this.emitConfig()},updatePaperTrader:function(t){this.paperTrader=t,this.paperTrader.enabled=!0,this.emitConfig()},emitConfig:function(){this.$emit("config",this.config)}}},b=w,C=(a("YEdZ"),Object(p["a"])(b,u,l,!1,null,null,null)),x=C.exports,S=a("MB/c"),T={components:{gekkoConfigBuilder:x,spinner:S["a"]},data:function(){return{pendingStratrunner:!1,config:{}}},computed:{gekkos:function(){return this.$store.state.gekkos},watchConfig:function(){var t=s.a.pick(this.config,"watch","candleWriter"),e=o["a"].util.extend({},t);return e.type="market watcher",e.mode="realtime",e},requiredHistoricalData:function(){if(this.config.tradingAdvisor&&this.config.valid){var t=this.config.tradingAdvisor;return t.candleSize*t.historySize}},gekkoConfig:function(){var t;if(this.existingMarketWatcher){if(this.requiredHistoricalData){var e=moment().utc().startOf("minute").subtract(this.requiredHistoricalData,"minutes").unix(),a=moment.utc(this.existingMarketWatcher.events.initial.candle.start).unix();t=moment.unix(Math.max(e,a)).utc().format()}else t=moment().utc().startOf("minute").format();var n=o["a"].util.extend({market:{type:"leech",from:t},mode:"realtime"},this.config);return n}},existingMarketWatcher:function(){var t=o["a"].util.extend({},this.watchConfig.watch);return s.a.find(this.gekkos,{config:{watch:t}})},exchange:function(){return this.watchConfig.watch.exchange},existingTradebot:function(){var t=this;return s.a.find(this.gekkos,function(e){return"tradebot"===e.logType&&e.config.watch.exchange===t.exchange})},availableApiKeys:function(){return this.$store.state.apiKeys}},watch:{existingMarketWatcher:function(t,e){var a=this;if(this.pendingStratrunner){var n=this.existingMarketWatcher;n.events.latest.candle&&(this.pendingStratrunner=!1,this.startGekko(function(t,e){a.$router.push({path:"/live-gekkos/".concat(e.id)})}))}}},methods:{updateConfig:function(t){this.config=t},start:function(){var t=this;if("tradebot"===this.config.type){if(this.existingTradebot){var e="You already have a tradebot running on this exchange";return e+=", you can only run one tradebot per exchange.",alert(e)}if(!this.availableApiKeys.includes(this.exchange))return alert("Please first configure API keys for this exchange in the config page.")}"market watcher"===this.config.type?this.existingMarketWatcher?(alert("This market is already being watched, redirecting you now..."),this.$router.push({path:"/live-gekkos/".concat(this.existingMarketWatcher.id)})):this.startWatcher(function(e,a){t.$router.push({path:"/live-gekkos/".concat(a.id)})}):this.existingMarketWatcher?this.startGekko(this.routeToGekko):this.startWatcher(function(e,a){t.pendingStratrunner=a.id})},routeToGekko:function(t,e){if(t||e.error)return console.error(t,e.error);this.$router.push({path:"/live-gekkos/".concat(e.id)})},startWatcher:function(t){Object(c["b"])("startGekko",this.watchConfig,t)},startGekko:function(t){Object(c["b"])("startGekko",this.gekkoConfig,t)}}},E=T,P=(a("2A8w"),Object(p["a"])(E,n,r,!1,null,null,null));e["a"]=P.exports},"2rY9":function(t,e,a){"use strict";var n=a("SWS5"),r=a.n(n);r.a},"5/bm":function(t,e,a){},"5shn":function(t,e,a){"use strict";var n=a("DlQD"),r=new n.Renderer;r.link=function(t,e,a){var n,r,i;return n=/^https?:\/\/.+$/.test(t),r=n||"newWindow"===e,i='"+a+""},n.setOptions({renderer:r}),e["a"]=n},"6BxS":function(t,e,a){"use strict";var n=function(){var t=this,e=t.$createElement,a=t._self._c||e;return a("div",[a("div",{staticClass:"mx1"},[a("label",{staticClass:"wrapper",attrs:{for:"exchange"}},[t._v("Exchange:")]),a("div",{staticClass:"custom-select button"},[a("select",{directives:[{name:"model",rawName:"v-model",value:t.exchange,expression:"exchange"}],on:{change:function(e){var a=Array.prototype.filter.call(e.target.options,function(t){return t.selected}).map(function(t){var e="_value"in t?t._value:t.value;return e});t.exchange=e.target.multiple?a:a[0]}}},t._l(t.exchanges,function(e,n){return a("option",[t._v(t._s(n))])}))])]),a("div",{staticClass:"grd-row"},[a("div",{staticClass:"grd-row-col-3-6 mx1"},[a("label",{attrs:{for:"currency"}},[t._v("Currency:")]),a("div",{staticClass:"custom-select button"},[a("select",{directives:[{name:"model",rawName:"v-model",value:t.currency,expression:"currency"}],on:{change:function(e){var a=Array.prototype.filter.call(e.target.options,function(t){return t.selected}).map(function(t){var e="_value"in t?t._value:t.value;return e});t.currency=e.target.multiple?a:a[0]}}},t._l(t.currencies,function(e){return a("option",[t._v(t._s(e))])}))])]),a("div",{staticClass:"grd-row-col-3-6 mx1"},[a("label",{attrs:{for:"asset"}},[t._v("Asset:")]),a("div",{staticClass:"custom-select button"},[a("select",{directives:[{name:"model",rawName:"v-model",value:t.asset,expression:"asset"}],on:{change:function(e){var a=Array.prototype.filter.call(e.target.options,function(t){return t.selected}).map(function(t){var e="_value"in t?t._value:t.value;return e});t.asset=e.target.multiple?a:a[0]}}},t._l(t.assets,function(e){return a("option",[t._v(t._s(e))])}))])])])])},r=[],i=(a("rGqo"),a("yt8O"),a("f3/d"),a("91GP"),a("LvDl")),s=a.n(i),o=(a("YIjs"),a("FhOJ"),a("wiDz"),{props:["onlyTradable","onlyImportable"],data:function(){return{exchange:"poloniex",currency:"USDT",asset:"BTC"}},created:function(){this.emitConfig()},computed:{exchanges:function(){var t=Object.assign({},this.$store.state.exchanges);return!s.a.isEmpty(t)&&(this.onlyTradable&&s.a.each(t,function(e,a){e.tradable||delete t[a]}),this.onlyImportable&&s.a.each(t,function(e,a){e.importable||delete t[a]}),t)},markets:function(){return this.exchanges?this.exchanges[this.exchange]:null},assets:function(){return this.exchanges?this.exchanges[this.exchange].markets[this.currency]:null},currencies:function(){return this.exchanges?s.a.keys(this.exchanges[this.exchange].markets):null},watchConfig:function(){return{watch:{exchange:this.exchange,currency:this.currency,asset:this.asset}}}},watch:{currency:function(){this.emitConfig()},asset:function(){this.emitConfig()},market:function(){this.emitConfig()},exchanges:function(){this.emitConfig()},exchange:function(){this.emitConfig()}},methods:{emitConfig:function(){this.$emit("market",this.watchConfig)}}}),c=o,u=a("KHd+"),l=Object(u["a"])(c,n,r,!1,null,null,null);e["a"]=l.exports},"6Wkr":function(t,e,a){"use strict";var n=function(){var t=this,e=t.$createElement,a=t._self._c||e;return a("div",{staticClass:"grd"},[a("div",{staticClass:"grd-row"},[a("div",{staticClass:"grd-row-col-3-6 px1"},[a("h3",[t._v("Strategy")]),a("div",[a("label",{staticClass:"wrapper",attrs:{for:"strat"}},[t._v("Strategy:")]),a("div",{staticClass:"custom-select button"},[a("select",{directives:[{name:"model",rawName:"v-model",value:t.strategy,expression:"strategy"}],on:{change:function(e){var a=Array.prototype.filter.call(e.target.options,function(t){return t.selected}).map(function(t){var e="_value"in t?t._value:t.value;return e});t.strategy=e.target.multiple?a:a[0]}}},t._l(t.strategies,function(e){return a("option",[t._v(t._s(e.name))])}))])]),a("div",[a("label",{attrs:{for:"candleSize"}},[t._v("Candle Size")]),a("div",{staticClass:"grd-row"},[a("div",{staticClass:"grd-row-col-3-6"},[a("input",{directives:[{name:"model",rawName:"v-model",value:t.rawCandleSize,expression:"rawCandleSize"}],domProps:{value:t.rawCandleSize},on:{input:function(e){e.target.composing||(t.rawCandleSize=e.target.value)}}})]),a("div",{staticClass:"grd-row-col-3-6 align"},[a("div",{staticClass:"custom-select button"},[a("select",{directives:[{name:"model",rawName:"v-model",value:t.candleSizeUnit,expression:"candleSizeUnit"}],on:{change:function(e){var a=Array.prototype.filter.call(e.target.options,function(t){return t.selected}).map(function(t){var e="_value"in t?t._value:t.value;return e});t.candleSizeUnit=e.target.multiple?a:a[0]}}},[a("option",[t._v("minutes")]),a("option",[t._v("hours")]),a("option",[t._v("days")])])])])])]),a("div",[a("label",{attrs:{for:"historySize"}},[t._v("Warmup period (in "+t._s(t.rawCandleSize)+" "+t._s(t.singularCandleSizeUnit)+" candles):")]),a("input",{directives:[{name:"model",rawName:"v-model",value:t.historySize,expression:"historySize"}],domProps:{value:t.historySize},on:{input:function(e){e.target.composing||(t.historySize=e.target.value)}}}),a("em",{staticClass:"label-like"},[t._v("(will use "+t._s(t.humanizeDuration(t.candleSize*t.historySize*1e3*60))+" of data as history)")])])]),a("div",{staticClass:"grd-row-col-3-6 px1"},[a("div",[a("h3",[t._v("Parameters")]),a("p",[t._v(t._s(t.strategy)+" Parameters:")]),a("textarea",{directives:[{name:"model",rawName:"v-model",value:t.rawStratParams,expression:"rawStratParams"}],staticClass:"params",domProps:{value:t.rawStratParams},on:{input:function(e){e.target.composing||(t.rawStratParams=e.target.value)}}}),t.rawStratParamsError?a("p",{staticClass:"bg--red p1"},[t._v(t._s(t.rawStratParamsError.message))]):t._e()])])])])},r=[],i=(a("dRSK"),a("LvDl")),s=a.n(i),o=a("wiDz"),c={data:function(){return{strategies:[],candleSizeUnit:"hours",rawCandleSize:1,strategy:"MACD",historySize:10,rawStratParams:"",rawStratParamsError:!1,emptyStrat:!1,stratParams:{}}},created:function(){var t=this;Object(o["a"])("strategies",function(e,a){t.strategies=a,s.a.each(t.strategies,function(t){t.empty=""===t.params}),t.rawStratParams=s.a.find(t.strategies,{name:t.strategy}).params,t.emptyStrat=s.a.find(t.strategies,{name:t.strategy}).empty,t.emitConfig()})},watch:{strategy:function(t){t=s.a.find(this.strategies,{name:t}),this.rawStratParams=t.params,this.emptyStrat=t.empty,this.emitConfig()},candleSize:function(){this.emitConfig()},historySize:function(){this.emitConfig()},rawStratParams:function(){this.emitConfig()}},computed:{candleSize:function(){return"minutes"===this.candleSizeUnit?this.rawCandleSize:"hours"===this.candleSizeUnit?60*this.rawCandleSize:"days"===this.candleSizeUnit?60*this.rawCandleSize*24:void 0},singularCandleSizeUnit:function(){return this.candleSizeUnit.slice(0,-1)},config:function(){var t={tradingAdvisor:{enabled:!0,method:this.strategy,candleSize:+this.candleSize,historySize:+this.historySize}};return this.emptyStrat?t[this.strategy]={__empty:!0}:t[this.strategy]=this.stratParams,t}},methods:{humanizeDuration:function(t){return window.humanizeDuration(t)},emitConfig:function(){this.parseParams(),this.$emit("stratConfig",this.config)},parseParams:function(){try{this.stratParams=toml.parse(this.rawStratParams),this.rawStratParamsError=!1}catch(t){this.rawStratParamsError=t,this.stratParams={}}}}},u=c,l=(a("tr8f"),a("KHd+")),d=Object(l["a"])(u,n,r,!1,null,null,null);e["a"]=d.exports},"7LpK":function(t,e,a){},"8KAT":function(t,e,a){},"9RND":function(t){t.exports={name:"gekko",version:"0.6.4",description:"A bitcoin trading bot for auto trading at various exchanges",keywords:["trading","bot","bitcoin","TA","finance"],scripts:{test:"./node_modules/.bin/mocha test/*.js --recursive test -u tdd --reporter spec",start:"node ./gekko --config config.js --ui"},author:"Mike van Rossum ",dependencies:{"@slack/client":"^3.10.0",async:"2.1.2","bitfinex-api-node":"^1.2.1","co-fs":"^1.2.0",commander:"^2.13.0",gekko:"0.0.9","humanize-duration":"^3.10.0",koa:"^1.2.0","koa-bodyparser":"^2.2.0","koa-cors":"0.0.16","koa-logger":"^1.3.0","koa-router":"^5.4.0","koa-static":"^2.0.0",lodash:"2.x",moment:"^2.20.1",opn:"^4.0.2","promisify-node":"^0.5.0","prompt-lite":"0.1.1",pushbullet:"1.4.3",relieve:"^2.1.3",retry:"^0.10.1",semver:"5.4.1",sqlite3:"^4.0.0","stats-lite":"^2.0.4","tiny-promisify":"^0.1.1",toml:"^2.3.0",twitter:"^1.7.1"},devDependencies:{chai:"^4.1.2",mocha:"^5.0.0",proxyquire:"^1.7.10",request:"^2.83.0","request-promise":"^4.2.2",sinon:"^4.2.0"},engines:{node:">=8.11.2"},license:"MIT",repository:{type:"git",url:"https://github.com/askmike/gekko.git"}}},CyGp:function(t,e,a){"use strict";var n=a("jTvs"),r=a.n(n);r.a},EAJ1:function(t,e,a){},EDI0:function(t,e,a){},EfWa:function(t,e,a){},FhOJ:function(t,e,a){"use strict";var n=function(){var t=this,e=t.$createElement,a=t._self._c||e;return a("div",[a("h3",[t._v("Daterange")]),a("div",[a("label",{attrs:{for:"from"}},[t._v("From")]),a("input",{directives:[{name:"model",rawName:"v-model",value:t.from,expression:"from"}],domProps:{value:t.from},on:{input:function(e){e.target.composing||(t.from=e.target.value)}}})]),a("div",[a("label",{attrs:{for:"to"}},[t._v("To")]),a("input",{directives:[{name:"model",rawName:"v-model",value:t.to,expression:"to"}],domProps:{value:t.to},on:{input:function(e){e.target.composing||(t.to=e.target.value)}}})])])},r=[],i=(a("LvDl"),a("wiDz"),{data:function(){return{from:"",to:""}},created:function(){var t=moment().startOf("minute"),e=t.clone().subtract(3,"months");this.to=this.fmt(t),this.from=this.fmt(e),this.emitRange()},methods:{fmtTs:function(t){return moment.unix(t).utc()},fmt:function(t){return t.utc().format("YYYY-MM-DD HH:mm")},emitRange:function(){this.$emit("range",{from:this.fmtTs(this.from),to:this.fmtTs(this.to)})},emitManualEntry:function(){if(this.from.length<"4"||this.from.length<"4")return this.$emit("range",{});var t=moment.utc(this.from),e=moment.utc(this.to);t.isValid()&&e.isValid()?this.$emit("range",{from:this.fmt(t),to:this.fmt(e)}):this.$emit("range",{})}},watch:{from:function(){this.emitManualEntry()},to:function(){this.emitManualEntry()},config:function(){this.scanned=!1},tab:function(){this.scanned=!1,this.$emit("range",{})},selectedRangeIndex:function(){var t=this.ranges[this.selectedRangeIndex];t&&this.emitRange(t)}}}),s=i,o=(a("TPp/"),a("KHd+")),c=Object(o["a"])(s,n,r,!1,null,null,null);e["a"]=c.exports},"H+ir":function(t,e,a){"use strict";var n=a("5/bm"),r=a.n(n);r.a},Kd0R:function(t,e,a){},"MB/c":function(t,e,a){"use strict";var n=function(){var t=this,e=t.$createElement;t._self._c;return t._m(0)},r=[function(){var t=this,e=t.$createElement,a=t._self._c||e;return a("div",{staticClass:"spinner"},[a("div",{staticClass:"rect1"}),a("div",{staticClass:"rect2"}),a("div",{staticClass:"rect3"}),a("div",{staticClass:"rect4"})])}],i={},s=i,o=(a("q431"),a("KHd+")),c=Object(o["a"])(s,n,r,!1,null,null,null);e["a"]=c.exports},Pf3K:function(t,e,a){"use strict";var n=function(){var t=this,e=t.$createElement,a=t._self._c||e;return a("div",{attrs:{id:"app"}},[a("top"),a("div",{staticClass:"fill"},[a("router-view",{staticClass:"view"})],1),a("bottom"),a("modal")],1)},r=[],i=function(){var t=this,e=t.$createElement,a=t._self._c||e;return a("div",[a("div",{attrs:{id:"top"}}),t._m(0),a("nav",{staticClass:"bg--light-gray"},[a("div",{staticClass:"menu contain"},[a("router-link",{staticClass:"py1",attrs:{to:"/home"}},[t._v("Home")]),a("router-link",{staticClass:"py1",attrs:{to:"/live-gekkos"}},[t._v("Live Gekkos")]),a("router-link",{staticClass:"py1",attrs:{to:"/backtest"}},[t._v("Backtest")]),a("router-link",{staticClass:"py1",attrs:{to:"/data"}},[t._v("Local data")]),a("router-link",{staticClass:"py1",attrs:{to:"/config"}},[t._v("Config")]),a("a",{staticClass:"py1",attrs:{href:"https://gekko.wizb.it/docs/introduction/about_gekko.html",target:"_blank"}},[t._v("Documentation")])],1)])])},s=[function(){var t=this,e=t.$createElement,a=t._self._c||e;return a("header",{staticClass:"bg--off-white grd"},[a("div",{staticClass:"contain grd-row"},[a("h3",{staticClass:"py1 px2 col-2"},[t._v("Gekko UI")])])])}],o={},c=o,u=(a("uMTv"),a("KHd+")),l=Object(u["a"])(c,i,s,!1,null,null,null),d=l.exports,f=function(){var t=this,e=t.$createElement,a=t._self._c||e;return a("footer",{staticClass:"p2 bg--off-white"},[a("div",{staticClass:"contain"},[t._m(0),a("p",[t._v("Using Gekko v"+t._s(t.version.gekko)+" and Gekko UI v"+t._s(t.version.ui)+".")])])])},h=[function(){var t=this,e=t.$createElement,a=t._self._c||e;return a("p",[a("em",[t._v("Use Gekko at your own risk.")])])}],m=a("9RND"),v=a("kiQV"),p={data:function(){return{version:{gekko:m.version,ui:v.version}}}},g=p,_=Object(u["a"])(g,f,h,!1,null,null,null),k=_.exports,y=function(){var t=this,e=t.$createElement,a=t._self._c||e;return t.active?a("div",[a("div",{attrs:{id:"modal-background"}}),a("div",{staticClass:"modal",attrs:{id:"modal"}},[a("div",{staticClass:"modal-guts",domProps:{innerHTML:t._s(t.content)}})])]):t._e()},w=[],b=a("5shn"),C={disconnected:Object(b["a"])("\n\n## Disconnected\n\nSomething happened to either Gekko or the connection.\nPlease check the terminal where Gekko is running or\nyour network connection.\n\n*This message is shown when the UI is unable to open a websocket connection with the Gekko Server.*\n\n ")},x={computed:{active:function(){return!this.$store.state.warnings.connected},content:function(){return this.$store.state.warnings.connected?"":C.disconnected}}},S=x,T=(a("TfKe"),Object(u["a"])(S,y,w,!1,null,null,null)),E=T.exports,P={name:"app",components:{top:d,bottom:k,modal:E}},A=P,z=(a("ZL7j"),Object(u["a"])(A,n,r,!1,null,null,null));e["a"]=z.exports},Q2AE:function(t,e,a){"use strict";var n={};a.r(n),a.d(n,"addImport",function(){return d}),a.d(n,"syncImports",function(){return f}),a.d(n,"updateImport",function(){return h});var r={};a.r(r),a.d(r,"syncGekkos",function(){return g}),a.d(r,"addGekko",function(){return _}),a.d(r,"updateGekko",function(){return k}),a.d(r,"archiveGekko",function(){return y}),a.d(r,"errorGekko",function(){return w}),a.d(r,"deleteGekko",function(){return b});var i={};a.r(i),a.d(i,"setGlobalWarning",function(){return C});var s={};a.r(s),a.d(s,"syncApiKeys",function(){return x}),a.d(s,"syncExchanges",function(){return S});a("VRzm");var o=a("Kw5r"),c=a("L2JU"),u=a("LvDl"),l=a.n(u),d=(a("INYr"),function(t,e){return t.imports.push(e),t}),f=function(t,e){return t.imports=e,t},h=function(t,e){var a=t.imports.findIndex(function(t){return t.id===e.import_id}),n=t.imports[a];if(!n)return t;var r=o["a"].util.extend(n,e.updates);return o["a"].set(t.imports,a,r),t},m=a("oyJW"),v=a("yT7P"),p=a("c7Wa"),g=function(t,e){return e?(t.gekkos=e.live,t.archivedGekkos=e.archive,t):t},_=function(t,e){return t.gekkos=Object(v["a"])({},t.gekkos,Object(m["a"])({},e.id,e)),t},k=function(t,e){return e.id&&l.a.has(t.gekkos,e.id)?(t.gekkos=Object(v["a"])({},t.gekkos,Object(m["a"])({},e.id,p(t.gekkos[e.id],e.event))),t):console.error("cannot update unknown gekko..")},y=function(t,e){return l.a.has(t.gekkos,e)?(t.archivedGekkos=Object(v["a"])({},t.archivedGekkos,Object(m["a"])({},e,Object(v["a"])({},t.gekkos[e],{stopped:!0,active:!1}))),t.gekkos=l.a.omit(t.gekkos,e),t):console.error("cannot archive unknown gekko..")},w=function(t,e){return l.a.has(t.gekkos,e.id)?(t.gekkos=Object(v["a"])({},t.gekkos,Object(m["a"])({},e.id,Object(v["a"])({},t.gekkos[e.id],{errored:!0,errorMessage:e.error}))),t):console.error("cannot error unknown gekko..")},b=function(t,e){return l.a.has(t.archivedGekkos,e)?(t.archivedGekkos=l.a.omit(t.archivedGekkos,e),t):console.error("cannot delete unknown gekko..")},C=function(t,e){return t.warnings[e.key]=e.value,t},x=function(t,e){return o["a"].set(t,"apiKeys",e),t},S=function(t,e){return o["a"].set(t,"exchanges",e),t};o["a"].use(c["a"]);var T=!1,E={};l.a.merge(E,n),l.a.merge(E,r),l.a.merge(E,i),l.a.merge(E,s);e["a"]=new c["a"].Store({state:{warnings:{connected:!0},imports:[],gekkos:{},archivedGekkos:{},connection:{disconnected:!1,reconnected:!1},apiKeys:[],exchanges:{}},mutations:E,strict:T})},Q6eY:function(t,e,a){"use strict";var n=a("SDwi"),r=a.n(n);r.a},SDwi:function(t,e,a){},SWS5:function(t,e,a){},T0Mt:function(t,e,a){"use strict";var n=a("pd3X"),r=a.n(n);r.a},TDb6:function(t,e,a){},"TPp/":function(t,e,a){"use strict";var n=a("7LpK"),r=a.n(n);r.a},TfKe:function(t,e,a){"use strict";var n=a("ZpQ2"),r=a.n(n);r.a},UeuA:function(t,e,a){"use strict";var n=function(){var t=this,e=t.$createElement,a=t._self._c||e;return a("div",{class:{clickable:!t.isClicked},attrs:{id:"chartWrapper"}},[a("div",{staticClass:"shield",on:{click:function(e){return e.preventDefault(),t.click(e)}}}),a("svg",{attrs:{id:"chart",width:"960",height:t.height}})])},r=[],i=a("k5N+"),s=a("LvDl"),o=a.n(s),c=function(t,e,a){var n=function(t){return o.a.isNumber(t)?moment.unix(t).utc().toDate():moment.utc(t).toDate()},r=e.map(function(t){return{price:t.price,date:n(t.date),action:t.action}}),s=t.map(function(t){return{price:t.open,date:n(t.start)}}),c=s.map(function(t){return+t.date}),u=s.map(function(t){return+t.price}),l=d3.select("#chart");l.attr("width",window.innerWidth-20);var d={top:20,right:20,bottom:110,left:40},f=a-d.top-d.bottom,h={top:a-70,right:20,bottom:30,left:40},m=+l.attr("width")-d.left-d.right,v=a-h.top-h.bottom,p=d3.scaleUtc().range([0,m]),g=d3.scaleUtc().range([0,m]),_=d3.scaleLinear().range([f,0]),k=d3.scaleLinear().range([v,0]),y=d3.axisBottom(p),w=d3.axisBottom(g),b=d3.axisLeft(_).ticks(a/50),C=d3.brushX().extent([[0,0],[m,v]]).on("brush end",z),x=d3.zoom().scaleExtent([1,100]).translateExtent([[0,0],[m,f]]).extent([[0,0],[m,f]]).on("zoom",$),S=d3.line().x(function(t){return p(t.date)}).y(function(t){return _(t.price)}),T=d3.line().x(function(t){return g(t.date)}).y(function(t){return k(t.price)});l.append("defs").append("clipPath").attr("id","clip").append("rect").attr("width",m).attr("height",f);var E=l.append("g").attr("class","focus").attr("transform","translate("+d.left+","+d.top+")"),P=l.append("g").attr("class","context").attr("transform","translate("+h.left+","+h.top+")");p.domain(d3.extent(s,function(t){return t.date})),_.domain([.99*d3.min(u),1.01*d3.max(u)]),g.domain(p.domain()),k.domain(_.domain()),E.append("path").datum(s).attr("class","line price").attr("d",S),E.append("g").attr("class","axis axis--x").attr("transform","translate(0,"+f+")").call(y),E.append("g").attr("class","axis axis--y").call(b),P.append("path").datum(s).attr("class","line").attr("d",T),P.append("g").attr("class","axis axis--x").attr("transform","translate(0,"+v+")").call(w);var A=l.append("g").attr("transform","translate("+d.left+","+d.top+")").selectAll("circle").data(r).enter().append("circle").attr("class",function(t){return t.action}).attr("cx",function(t){return p(t.date)}).attr("cy",function(t){return _(t.price)}).attr("r",5);P.append("g").selectAll("circle").data(r).enter().append("circle").attr("class",function(t){return t.action}).attr("cx",function(t){return g(t.date)}).attr("cy",function(t){return k(t.price)}).attr("r",3);function z(){if(!d3.event.sourceEvent||"zoom"!==d3.event.sourceEvent.type){var t=d3.event.selection||g.range();p.domain(t.map(g.invert,g)),D(p.domain()),l.select(".axis--y").call(b),A.attr("cx",function(t){return p(t.date)}).attr("cy",function(t){return _(t.price)}),E.select(".line").attr("d",S),E.select(".axis--x").call(y),l.select(".zoom").call(x.transform,d3.zoomIdentity.scale(m/(t[1]-t[0])).translate(-t[0],0))}}function D(t){var e=Object(i["a"])(t,2),a=e[0],n=e[1],r=o.a.sortedIndex(c,a),s=o.a.sortedIndex(c,n),l=u.slice(r,s);_.domain([.9995*d3.min(l),1.0005*d3.max(l)])}function $(){if(!d3.event.sourceEvent||"brush"!==d3.event.sourceEvent.type){var t=d3.event.transform;D(t.rescaleX(g).domain()),l.select(".axis--y").call(b),p.domain(t.rescaleX(g).domain()),E.select(".line").attr("d",S),A.attr("cx",function(t){return p(t.date)}).attr("cy",function(t){return _(t.price)}),E.select(".axis--x").call(y),P.select(".brush").call(C.move,p.range().map(t.invertX,t))}}P.append("g").attr("class","brush").call(C).call(C.move,p.range()),l.append("rect").attr("class","zoom").attr("width",m).attr("height",f).attr("transform","translate("+d.left+","+d.top+")").call(x)},u=(a("dRSK"),function(t){d3.select("#chart").append("text").attr("class","message").attr("x",150).attr("y",150).text(t)}),l=4,d={props:["data","height"],data:function(){return{isClicked:!1}},watch:{data:function(){this.render()}},created:function(){setTimeout(this.render,100)},beforeDestroy:function(){this.remove()},methods:{click:function(){this.isClicked=!0},render:function(){this.remove(),_.size(this.data.candles)0?"profit":"loss"}}},$=D,j=(a("WlXR"),Object(g["a"])($,P,A,!1,null,null,null)),O=j.exports,M=a("UeuA"),G=a("vf3E"),I={props:["result"],data:function(){return{}},methods:{},components:{roundtripTable:G["a"],resultSummary:O,chart:M["a"]},computed:{candles:function(){return{candles:this.result.stratCandles,trades:this.result.trades}}}},R=I,H=(a("wVP4"),Object(g["a"])(R,T,E,!1,null,null,null)),K=H.exports,Y={data:function(){return{backtestable:!1,backtestState:"idle",backtestResult:!1,config:!1}},methods:{check:function(t){if(this.config=t,!t.valid)return this.backtestable=!1;this.backtestable=!0},run:function(){var t=this;this.backtestState="fetching",Object(f["b"])("backtest",this.config,function(e,a){t.backtestState="fetched",t.backtestResult=a})}},components:{configBuilder:S,result:K,spinner:h["a"]}},W=Y,N=Object(g["a"])(W,n,r,!1,null,null,null);e["a"]=N.exports},c7Wa:function(t,e){const a=["marketUpdate"],n=["marketStart","stratWarmupCompleted"],r=["tradeCompleted","advice","roundtrip"],i=(t,e)=>{const i=e.type,s=e.payload;return t={...t,latestUpdate:new Date},r.includes(i)&&(t=t.events[i]?{...t,events:{...t.events,[i]:[...t.events[i],s]}}:{...t,events:{...t.events,[i]:[s]}}),t.events.initial[i]||a.includes(i)||(t={...t,events:{...t.events,initial:{...t.events.initial,[i]:s}}}),n.includes(i)||(t={...t,events:{...t.events,latest:{...t.events.latest,[i]:s}}}),t};t.exports=i},dHeD:function(t,e,a){"use strict";var n=function(){var t=this,e=t.$createElement,a=t._self._c||e;return a("div",{staticClass:"contain"},[a("div",{staticClass:"text",domProps:{innerHTML:t._s(t.intro)}}),a("div",{staticClass:"hr"}),a("h2",[t._v("Available datasets")]),"idle"===t.datasetScanstate?a("div",{staticClass:"txt--center my2"},[a("a",{staticClass:"w100--s btn--primary scan-btn",attrs:{href:"#"},on:{click:function(e){return e.preventDefault(),t.scan(e)}}},[t._v("Scan available data")])]):t._e(),"scanning"===t.datasetScanstate?a("div",{staticClass:"txt--center my2"},[a("spinner")],1):t._e(),"scanned"===t.datasetScanstate?a("div",{staticClass:"my2"},[t.unscannableMakets.length?a("div",{staticClass:"bg--orange p1 warning my1"},[t.viewUnscannable?t._e():a("p",{staticClass:"clickable",on:{click:function(e){return e.preventDefault(),t.toggleUnscannable(e)}}},[t._v("Some markets were unscannable, click here for details.")]),t.viewUnscannable?[a("p",[t._v("Unable to find datasets in the following markets:")]),t._l(t.unscannableMakets,function(e){return a("div",{staticClass:"mx2"},[t._v("- "+t._s(e.exchange)+":"+t._s(e.currency)+":"+t._s(e.asset))])})]:t._e()],2):t._e(),t.datasets.length?[a("table",{staticClass:"full data"},[t._m(0),a("tbody",t._l(t.datasets,function(e){return a("tr",[a("td",[t._v(t._s(e.exchange))]),a("td",[t._v(t._s(e.currency))]),a("td",[t._v(t._s(e.asset))]),a("td",[t._v(t._s(t.fmt(e.from)))]),a("td",[t._v(t._s(t.fmt(e.to)))]),a("td",[t._v(t._s(t.humanizeDuration(e.to.diff(e.from))))])])}))])]:t._e(),t.datasets.length?t._e():[a("p",[t._v("It looks like you don't have any local data yet.")])]],2):t._e(),a("div",{staticClass:"my2"},[a("h2",[t._v("Import more data")]),a("p",{staticClass:"text"},[t._v("You can easily import more market data directly from exchanges using the importer.")]),a("router-link",{staticClass:"btn--primary",attrs:{to:"/data/importer"}},[t._v("Go to the importer!")])],1)])},r=[function(){var t=this,e=t.$createElement,a=t._self._c||e;return a("thead",[a("tr",[a("th",[t._v("exchange")]),a("th",[t._v("currency")]),a("th",[t._v("asset")]),a("th",[t._v("from")]),a("th",[t._v("to")]),a("th",[t._v("duration")])])])}],i=(a("VRzm"),a("MB/c")),s=a("5shn"),o=a("spvI"),c=Object(s["a"])("\n\n## Local data\n\nGekko needs local market data in order to backtest strategies. The local\ndata can also be used in a warmup period when running a strategy against a\nlive market.\n\n"),u={mixins:[o["a"]],components:{spinner:i["a"]},data:function(){return{intro:c,viewUnscannable:!1}},methods:{toggleUnscannable:function(){this.viewUnscannable=!0},humanizeDuration:function(t){return window.humanizeDuration(t)},fmt:function(t){return t.format("YYYY-MM-DD HH:mm")}}},l=u,d=(a("2rY9"),a("KHd+")),f=Object(d["a"])(l,n,r,!1,null,null,null);e["a"]=f.exports},dW8q:function(t,e,a){"use strict";var n=a("w3yp"),r=a.n(n);r.a},daul:function(t,e,a){"use strict";var n=a("Kd0R"),r=a.n(n);r.a},g298:function(t,e,a){"use strict";var n=function(){var t=this,e=t.$createElement,a=t._self._c||e;return a("div",{staticClass:"contain my2"},[t.data&&!t.data.done?a("div",[a("h2",[t._v("Importing data..")]),a("div",{staticClass:"grd"},[a("div",{staticClass:"grd-row"},[t._m(0),a("div",{staticClass:"grd-row-col-4-6"},[t._v(t._s(t.data.watch.exchange))])]),a("div",{staticClass:"grd-row"},[t._m(1),a("div",{staticClass:"grd-row-col-4-6"},[t._v(t._s(t.data.watch.currency)+"/"+t._s(t.data.watch.asset))])])]),a("div",{staticClass:"grd"},[a("div",{staticClass:"grd-row"},[t._m(2),a("div",{staticClass:"grd-row-col-4-6"},[t._v(t._s(t.fmt(t.from)))])]),a("div",{staticClass:"grd-row"},[t._m(3),a("div",{staticClass:"grd-row-col-4-6"},[t._v(t._s(t.fmt(t.to)))])]),t.initialized?a("div",{staticClass:"grd-row"},[t._m(4),a("div",{staticClass:"grd-row-col-4-6"},[t._v(t._s(t.fmt(t.latest)))])]):t._e(),t.initialized?a("div",{staticClass:"grd-row"},[t._m(5),a("div",{staticClass:"grd-row-col-4-6"},[t._v(t._s(t.fromEnd))])]):t._e()]),t.initialized?t._e():a("spinner"),t.initialized?a("div",{staticClass:"contain"},[a("progressBar",{attrs:{progress:t.progress}})],1):t._e(),a("p",[a("em",[t._v("(you don't have to wait until the import is done,you can already start "),a("router-link",{attrs:{to:"/backtest"}},[t._v("backtesting")]),t._v(").")],1)])],1):t._e(),t.data&&t.data.done?a("div",{staticClass:"txt--center"},[a("h2",[t._v("Import done")]),a("p",[t._v(" Go and "),a("router-link",{attrs:{to:"/backtest"}},[t._v("backtest")]),t._v(" with your new data!")],1)]):t._e(),t.data?t._e():a("div",{staticClass:"txt--center"},[a("h2",[t._v("ERROR: Unknown import")]),a("p",[a("I",[t._v("don't know this import..")])],1)])])},r=[function(){var t=this,e=t.$createElement,a=t._self._c||e;return a("div",{staticClass:"grd-row-col-2-6"},[a("strong",[t._v("Market:")])])},function(){var t=this,e=t.$createElement,a=t._self._c||e;return a("div",{staticClass:"grd-row-col-2-6"},[a("strong",[t._v("Currency/Asset:")])])},function(){var t=this,e=t.$createElement,a=t._self._c||e;return a("div",{staticClass:"grd-row-col-2-6"},[a("strong",[t._v("From:")])])},function(){var t=this,e=t.$createElement,a=t._self._c||e;return a("div",{staticClass:"grd-row-col-2-6"},[a("strong",[t._v("To:")])])},function(){var t=this,e=t.$createElement,a=t._self._c||e;return a("div",{staticClass:"grd-row-col-2-6"},[a("strong",[t._v("Imported data until:")])])},function(){var t=this,e=t.$createElement,a=t._self._c||e;return a("div",{staticClass:"grd-row-col-2-6"},[a("strong",[t._v("Remaining:")])])}],i=(a("dRSK"),a("LvDl")),s=a.n(i),o=function(){var t=this,e=t.$createElement,a=t._self._c||e;return t.progress?a("div",{staticClass:"progressbarWrapper"},[a("p",[a("strong",[t._v(t._s(t.round(t.progress))+"%")])]),a("div",{staticClass:"progressbar"},[a("div",{style:{width:t.progress+"%"}})])]):t._e()},c=[],u={props:["progress"],methods:{round:function(t){return(+t).toFixed(2)}}},l=u,d=(a("/vJE"),a("KHd+")),f=Object(d["a"])(l,o,c,!1,null,null,null),h=f.exports,m=a("MB/c"),v={components:{progressBar:h,spinner:m["a"]},computed:{data:function(){return s.a.find(this.$store.state.imports,{id:this.$route.params.id})},initialized:function(){if(this.data&&this.latest.isValid())return!0},latest:function(){if(this.data)return this.mom(this.data.latest)},fromEndMs:function(){if(this.data)return this.to.diff(this.latest)},fromEnd:function(){return this.latest?humanizeDuration(this.fromEndMs):"LOADING"},from:function(){if(this.data)return this.mom(this.data.from)},to:function(){if(this.data)return this.mom(this.data.to)},timespan:function(){if(this.data)return this.to.diff(this.from)},progress:function(){if(this.data){var t=this.timespan-this.fromEndMs;return 100*t/this.timespan}}},methods:{fmt:function(t){return t.format("YYYY-MM-DD HH:mm:ss")},mom:function(t){return moment.utc(t)}}},p=v,g=(a("qDq4"),Object(d["a"])(p,n,r,!1,null,null,null));e["a"]=g.exports},gYof:function(t,e,a){"use strict";a("LvDl");var n=a("Kw5r"),r=a("r0f2"),i=(a("L2JU"),a("wiDz")),s=a("Q2AE"),o=function(){Object(i["a"])("imports",function(t,e){s["a"].commit("syncImports",e)})},c=function(){C.$on("import_update",function(t){s["a"].commit("updateImport",t)})},u=function(){o(),c()},l=function(){Object(i["a"])("gekkos",function(t,e){var a=e;s["a"].commit("syncGekkos",a)})},d=function(){C.$on("gekko_new",function(t){return s["a"].commit("addGekko",t.state)}),C.$on("gekko_event",function(t){return s["a"].commit("updateGekko",t)}),C.$on("gekko_archived",function(t){return s["a"].commit("archiveGekko",t.id)}),C.$on("gekko_error",function(t){return s["a"].commit("errorGekko",t)}),C.$on("gekko_deleted",function(t){return s["a"].commit("deleteGekko",t.id)})},f=function(){l(),d()},h=function(){},m=function(){C.$on("WS_STATUS_CHANGE",function(t){return s["a"].commit("setGlobalWarning",{key:"connected",value:t.connected})})},v=function(){h(),m()},p=a("k5N+"),g=(a("rGqo"),function(t){if(!t)return{};var e=t,a={};return e.forEach(function(t){a[t.slug]=a[t.slug]||{markets:{}},t.markets.forEach(function(e){var n=Object(p["a"])(e["pair"],2),r=n[0],i=n[1];a[t.slug].markets[r]=a[t.slug].markets[r]||[],a[t.slug].markets[r].push(i)}),"exchangeMaxHistoryAge"in t&&(a[t.slug].exchangeMaxHistoryAge=t.exchangeMaxHistoryAge),a[t.slug].importable=!!t.providesFullHistory,a[t.slug].tradable=!!t.tradable,a[t.slug].requires=t.requires}),a}),_=function(){Object(i["a"])("apiKeys",function(t,e){s["a"].commit("syncApiKeys",e)}),Object(i["a"])("exchanges",function(t,e){s["a"].commit("syncExchanges",g(e))})},k=function(){C.$on("apiKeys",function(t){s["a"].commit("syncApiKeys",t.exchanges)})},y=function(){_(),k()},w=function(){u(),f(),v(),y()};a.d(e,"a",function(){return C}),a.d(e,"b",function(){return S});var b=null,C=new n["a"];C.$on("gekko_update",function(t){return console.log(t)}),C.$on("import_update",function(t){return console.log(t)}),C.$on("import_error",function(t){alert("IMPORT ERROR: "+t.error)});var x={connected:!1},S=function(){b=new ReconnectingWebSocket(r["b"],null,{maxReconnectInterval:4e3}),setTimeout(function(){x.connected||(w(),C.$emit("WS_STATUS_CHANGE",x))},500),b.onopen=function(){x.connected||(x.connected=!0,C.$emit("WS_STATUS_CHANGE",x),w())},b.onclose=function(){x.connected&&(x.connected=!1,C.$emit("WS_STATUS_CHANGE",x))},b.onerror=function(){x.connected&&(x.connected=!1,C.$emit("WS_STATUS_CHANGE",x))},b.onmessage=function(t){var e=JSON.parse(t.data);C.$emit(e.type,e)}}},h8ks:function(t,e,a){},hGnM:function(t,e,a){"use strict";var n=function(){var t=this,e=t.$createElement,a=t._self._c||e;return a("div",{staticClass:"contain py2"},[a("h3",[t._v("Market watchers")]),t.watchers.length?t._e():a("div",{staticClass:"text"},[a("p",[t._v("You don't have any market watchers.")])]),t.watchers.length?a("table",{staticClass:"full clickable"},[t._m(0),a("tbody",t._l(t.watchers,function(e){return a("tr",{staticClass:"clickable",on:{click:function(a){t.$router.push({path:"/live-gekkos/"+e.id})}}},[a("td",[t._v(t._s(e.config.watch.exchange))]),a("td",[t._v(t._s(e.config.watch.currency))]),a("td",[t._v(t._s(e.config.watch.asset))]),a("td",[t._v(t._s(t.status(e)))]),a("td",[e.events.initial.candle?[t._v(t._s(t.fmt(e.events.initial.candle.start)))]:t._e()],2),a("td",[e.events.latest.candle?[t._v(t._s(t.fmt(e.events.latest.candle.start)))]:t._e()],2),a("td",[e.events.initial.candle&&e.events.latest.candle?[t._v(t._s(t.timespan(e.events.latest.candle.start,e.events.initial.candle.start)))]:t._e()],2)])}))]):t._e(),a("h3",[t._v("Strat runners")]),t.stratrunners.length?t._e():a("div",{staticClass:"text"},[a("p",[t._v("You don't have any stratrunners.")])]),t.stratrunners.length?a("table",{staticClass:"full"},[t._m(1),a("tbody",t._l(t.stratrunners,function(e){return a("tr",{staticClass:"clickable",on:{click:function(a){t.$router.push({path:"/live-gekkos/"+e.id})}}},[a("td",[t._v(t._s(e.config.watch.exchange))]),a("td",[t._v(t._s(e.config.watch.currency))]),a("td",[t._v(t._s(e.config.watch.asset))]),a("td",[t._v(t._s(t.status(e)))]),a("td",[e.events.initial.candle&&e.events.latest.candle?[t._v(t._s(t.timespan(e.events.latest.candle.start,e.events.initial.candle.start)))]:t._e()],2),a("td",[t._v(t._s(e.config.tradingAdvisor.method))]),a("td",[t.report(e)?t._e():[t._v("0")],t.report(e)?[t._v(t._s(t.round(t.report(e).profit))+" "+t._s(t.report(e).currency))]:t._e()],2),a("td",[t._v(t._s(e.logType))]),a("td",[e.events.tradeCompleted?t._e():[t._v("0")],e.events.tradeCompleted?[t._v(t._s(e.events.tradeCompleted.length))]:t._e()],2)])}))]):t._e(),a("div",{staticClass:"hr"}),a("h2",[t._v("Start a new live Gekko")]),a("router-link",{staticClass:"btn--primary",attrs:{to:"/live-gekkos/new"}},[t._v("Start a new live Gekko!")])],1)},r=[function(){var t=this,e=t.$createElement,a=t._self._c||e;return a("thead",[a("tr",[a("th",[t._v("exchange")]),a("th",[t._v("currency")]),a("th",[t._v("asset")]),a("th",[t._v("status")]),a("th",[t._v("started at")]),a("th",[t._v("last update")]),a("th",[t._v("duration")])])])},function(){var t=this,e=t.$createElement,a=t._self._c||e;return a("thead",[a("tr",[a("th",[t._v("exchange")]),a("th",[t._v("currency")]),a("th",[t._v("asset")]),a("th",[t._v("status")]),a("th",[t._v("duration")]),a("th",[t._v("strategy")]),a("th",[t._v("PnL")]),a("th",[t._v("type")]),a("th",[t._v("trades")])])])}],i=(a("a1Th"),a("rGqo"),a("yt8O"),a("VRzm"),{created:function(){var t=this;this.timer=setInterval(function(){t.now=moment()},1e3)},destroyed:function(){clearTimeout(this.timer)},data:function(){return{timer:!1,now:moment()}},computed:{stratrunners:function(){return _.values(this.$store.state.gekkos).concat(_.values(this.$store.state.archivedGekkos)).filter(function(t){return"papertrader"===t.logType||"tradebot"===t.logType})},watchers:function(){return _.values(this.$store.state.gekkos).concat(_.values(this.$store.state.archivedGekkos)).filter(function(t){return"watcher"===t.logType})}},methods:{humanizeDuration:function(t){return window.humanizeDuration(t)},moment:function(t){function e(e){return t.apply(this,arguments)}return e.toString=function(){return t.toString()},e}(function(t){return moment.utc(t)}),fmt:function(t){return moment.utc(t).format("YYYY-MM-DD HH:mm")},round:function(t){return(+t).toFixed(3)},timespan:function(t,e){return this.humanizeDuration(this.moment(t).diff(this.moment(e)))},status:function(t){return t.errored?"errored":t.stopped?"stopped":t.active?"running":void console.log("unknown state:",t)},report:function(t){return _.get(t,"events.latest.performanceReport")}}}),s=i,o=(a("daul"),a("KHd+")),c=Object(o["a"])(s,n,r,!1,null,null,null);e["a"]=c.exports},jTvs:function(t,e,a){},jVW9:function(t,e,a){},jf14:function(t,e,a){},jloR:function(t,e,a){"use strict";var n=function(){var t=this,e=t.$createElement,a=t._self._c||e;return a("div",{staticClass:"contain my2"},[a("div",{staticClass:"text",domProps:{innerHTML:t._s(t.intro)}}),a("div",{staticClass:"hr"}),a("h3",[t._v("Currently running imports")]),0===t.imports.length?a("p",[t._v("You currently don't have any imports running.")]):t._e(),t.imports.length?a("ul",t._l(t.imports,function(e){return a("li",[a("router-link",{attrs:{to:"/data/importer/import/"+e.id}},[t._v(t._s(e.watch.exchange)+":"+t._s(e.watch.currency)+"/"+t._s(e.watch.asset))])],1)})):t._e(),a("div",{staticClass:"hr"}),a("h3",[t._v("Start a new import")]),a("import-config-builder",{on:{config:t.updateConfig}}),a("div",{staticClass:"hr"}),a("div",{staticClass:"txt--center"},[a("a",{staticClass:"w100--s my1 btn--primary",attrs:{href:"#"},on:{click:function(e){return e.preventDefault(),t.run(e)}}},[t._v("Import")])])],1)},r=[],i=a("wiDz"),s=a("MB/c"),o=function(){var t=this,e=t.$createElement,a=t._self._c||e;return a("div",{staticClass:"grd contain"},[a("div",{staticClass:"grd-row"},[a("div",{staticClass:"grd-row-col-3-6 mx1"},[a("h3",[t._v("Market")]),a("market-picker",{attrs:{"only-importable":"true"},on:{market:t.updateMarketConfig}})],1),a("div",{staticClass:"grd-row-col-3-6 mx1"},[a("range-creator",{on:{range:t.updateRange}})],1)])])},c=[],u=(a("91GP"),a("6BxS")),l=a("FhOJ"),d=(a("LvDl"),{data:function(){return{market:{},range:{}}},components:{marketPicker:u["a"],rangeCreator:l["a"]},computed:{config:function(){var t={};return Object.assign(t,this.market,{importer:{daterange:this.range}},{candleWriter:{enabled:!0}}),t}},methods:{updateMarketConfig:function(t){this.market=t,this.emitConfig()},updateRange:function(t){this.range=t,this.emitConfig()},emitConfig:function(){this.$emit("config",this.config)}}}),f=d,h=(a("H+ir"),a("KHd+")),m=Object(h["a"])(f,o,c,!1,null,null,null),v=m.exports,p=a("5shn"),g=Object(p["a"])("\n\n## Import data\n\nThe importer can download historical market data directly from the exchange.\n\n"),_={components:{importConfigBuilder:v,spinner:s["a"]},data:function(){return{intro:g,config:{}}},computed:{imports:function(){return this.$store.state.imports}},methods:{daysApart:function(t){var e=moment(t.to),a=moment(t.from);return e.diff(a,"days")},updateConfig:function(t){this.config=t},run:function(){var t=this,e=this.daysApart(this.config.importer.daterange);if(e<1)return alert("You can only import at least one day of data..");var a=this.$store.state.exchanges[this.config.watch.exchange];if("exchangeMaxHistoryAge"in a&&moment(this.config.importer.daterange.from) 1%","last 2 versions","not ie <= 8"]}},ldlP:function(t,e,a){"use strict";var n=function(){var t=this,e=t.$createElement,a=t._self._c||e;return a("section",{staticClass:"contain grd-row"},[a("div",{staticClass:"grd-row-col-3-6",domProps:{innerHTML:t._s(t.left)}}),t._m(0)])},r=[function(){var t=this,e=t.$createElement,a=t._self._c||e;return a("div",{staticClass:"grd-row-col-3-6 txt--center"},[a("img",{attrs:{src:"static/gekko.jpg"}}),a("p",[a("em",[t._v("The most valuable commodity I know of is information.")])])])}],i=(a("VRzm"),a("5shn")),s=Object(i["a"])("\n\n## Gekko\n\nGekko is a Bitcoin trading bot and backtesting platform that\nconnects to popular Bitcoin exchanges. It is written in javascript\nand runs on nodejs.\n\n[Find out more](https://gekko.wizb.it/).\n\n*Gekko is 100% free (open source), if you paid for this you have been scammed.*\n\n"),o={data:function(){return{left:s}}},c=o,u=a("KHd+"),l=Object(u["a"])(c,n,r,!1,null,null,null);e["a"]=l.exports},mf2E:function(t,e,a){"use strict";var n=function(){var t=this,e=t.$createElement,a=t._self._c||e;return a("div",{staticClass:"my2"},[t.data?t._e():a("div",{staticClass:"contain"},[a("h1",[t._v("Unknown Gekko instance")]),a("p",[t._v("Gekko doesn't know what gekko this is...")])]),t.data?a("div",[a("h2",{staticClass:"contain"},[t._v("Gekko "+t._s(t.type))]),t.isArchived?a("div",{staticClass:"contain brdr--mid-gray p1 bg--orange"},[t._v("This is an archived Gekko, it is currently not running anymore.")]):t._e(),t.data.errorMessage?a("div",{staticClass:"contain brdr--mid-gray p1 bg--orange"},[t._v("This is Gekko crashed with the following error: "+t._s(t.data.errorMessage))]):t._e(),a("div",{staticClass:"grd contain"},[a("div",{staticClass:"grd-row"},[a("div",{staticClass:"grd-row-col-3-6"},[a("h3",[t._v("Market")]),a("div",{staticClass:"grd-row"},[a("div",{staticClass:"grd-row-col-3-6"},[t._v("Exchange")]),a("div",{staticClass:"grd-row-col-3-6"},[t._v(t._s(t.config.watch.exchange))])]),a("div",{staticClass:"grd-row"},[a("div",{staticClass:"grd-row-col-3-6"},[t._v("Currency")]),a("div",{staticClass:"grd-row-col-3-6"},[t._v(t._s(t.config.watch.currency))])]),a("div",{staticClass:"grd-row"},[a("div",{staticClass:"grd-row-col-3-6"},[t._v("Asset")]),a("div",{staticClass:"grd-row-col-3-6"},[t._v(t._s(t.config.watch.asset))])]),a("div",{staticClass:"grd-row"},[a("div",{staticClass:"grd-row-col-3-6"},[t._v("Type")]),a("div",{staticClass:"grd-row-col-3-6"},[t._v(t._s(t.type))])])]),a("div",{staticClass:"grd-row-col-3-6"},[a("h3",[t._v("Runtime")]),t.isLoading?a("spinner"):t._e(),t.isLoading?t._e():[t.initialEvents.candle?a("div",{staticClass:"grd-row"},[a("div",{staticClass:"grd-row-col-2-6"},[t._v("Watching since")]),a("div",{staticClass:"grd-row-col-4-6"},[t._v(t._s(t.fmt(t.initialEvents.candle.start)))])]):t._e(),t.latestEvents.candle?a("div",{staticClass:"grd-row"},[a("div",{staticClass:"grd-row-col-2-6"},[t._v("Received data until")]),a("div",{staticClass:"grd-row-col-4-6"},[t._v(t._s(t.fmt(t.latestEvents.candle.start)))])]):t._e(),t.latestEvents.candle?a("div",{staticClass:"grd-row"},[a("div",{staticClass:"grd-row-col-2-6"},[t._v("Data spanning")]),a("div",{staticClass:"grd-row-col-4-6"},[t._v(t._s(t.humanizeDuration(t.moment(t.latestEvents.candle.start).diff(t.moment(t.initialEvents.candle.start)))))])]):t._e(),t.isStratrunner?[a("div",{staticClass:"grd-row"},[a("div",{staticClass:"grd-row-col-2-6"},[t._v("Amount of trades")]),a("div",{staticClass:"grd-row-col-4-6"},[t._v(t._s(t.trades.length))])]),a("div",{staticClass:"grd-row"},[a("div",{staticClass:"grd-row-col-2-6"},[t._v("Candle size")]),a("div",{staticClass:"grd-row-col-4-6"},[t._v(t._s(t.config.tradingAdvisor.candleSize))])]),a("div",{staticClass:"grd-row"},[a("div",{staticClass:"grd-row-col-2-6"},[t._v("History size")]),a("div",{staticClass:"grd-row-col-4-6"},[t._v(t._s(t.config.tradingAdvisor.historySize))])])]:t._e()]],2)]),t.warmupRemaining?a("div",{staticClass:"contain brdr--mid-gray p1 bg--orange"},[t._v("This stratrunner is still warming up for the next "),a("i",[t._v(t._s(t.warmupRemaining.replace(","," and ")))]),t._v(", it will not trade until it is warmed up.")]):t._e(),t.isStratrunner?a("div",{staticClass:"grd-row"},[a("div",{staticClass:"grd-row-col-3-6"},[a("h3",[t._v("Strategy")]),a("div",{staticClass:"grd-row"},[a("div",{staticClass:"grd-row-col-3-6"},[t._v("Name")]),a("div",{staticClass:"grd-row-col-3-6"},[a("strong",[t._v(t._s(t.stratName))])])]),t._v("Parameters"),a("pre",[t._v(t._s(t.stratParams))])]),a("div",{staticClass:"grd-row-col-3-6"},[a("h3",[t._v("Profit report")]),t.report?t._e():[a("p",[t.isArchived?a("em",[t._v("This Gekko never executed a trade..")]):t._e(),t.isArchived?t._e():a("em",[t._v("Waiting for at least one trade..")])])],t.report?[a("div",{staticClass:"grd-row"},[a("div",{staticClass:"grd-row-col-3-6"},[t._v("Start balance")]),a("div",{staticClass:"grd-row-col-3-6"},[t._v(t._s(t.round(t.report.startBalance)))])]),a("div",{staticClass:"grd-row"},[a("div",{staticClass:"grd-row-col-3-6"},[t._v("Current balance")]),a("div",{staticClass:"grd-row-col-3-6"},[t._v(t._s(t.round(t.report.balance)))])]),a("div",{staticClass:"grd-row"},[a("div",{staticClass:"grd-row-col-3-6"},[t._v("Market")]),a("div",{staticClass:"grd-row-col-3-6"},[t._v(t._s(t.round(t.report.market/100*t.report.startPrice))+" "+t._s(t.config.watch.currency)+" ("+t._s(t.round(t.report.market))+" %)")])]),a("div",{staticClass:"grd-row"},[a("div",{staticClass:"grd-row-col-3-6"},[t._v("Profit")]),a("div",{staticClass:"grd-row-col-3-6"},[t._v(t._s(t.round(t.report.profit))+" "+t._s(t.config.watch.currency)+" ("+t._s(t.round(t.report.relativeProfit))+" %)")])]),a("div",{staticClass:"grd-row"},[a("div",{staticClass:"grd-row-col-3-6"},[t._v("Alpha")]),a("div",{staticClass:"grd-row-col-3-6"},[t._v(t._s(t.round(t.report.alpha))+" "+t._s(t.config.watch.currency))])])]:t._e()],2)]):t._e(),!t.isStratrunner||t.watcher||t.isArchived?t._e():a("p",[t._v("WARNING: stale gekko, not attached to a watcher, please report "),a("a",{attrs:{href:"https://github.com/askmike/gekko/issues"}},[t._v("here")]),t._v(".")]),t.isArchived?t._e():a("p",[a("a",{staticClass:"w100--s my1 btn--red",on:{click:t.stopGekko}},[t._v("Stop Gekko")])]),t.isArchived?a("p",[a("a",{staticClass:"w100--s my1 btn--red",on:{click:t.deleteGekko}},[t._v("Delete Gekko")])]):t._e(),t.isStratrunner&&t.watcher&&!t.isArchived?a("p",[a("em",[t._v("This gekko gets market data from "),a("router-link",{attrs:{to:"/live-gekkos/"+t.watcher.id}},[t._v("this market watcher")])],1),t._v(".")]):t._e()]),t.isLoading?t._e():[a("h3",{staticClass:"contain"},[t._v("Market graph")]),"fetching"===t.candleFetch?a("spinner"):t._e(),"fetched"===t.candleFetch?[a("chart",{attrs:{data:t.chartData,height:300}})]:t._e(),t.isStratrunner?a("roundtrips",{attrs:{roundtrips:t.roundtrips}}):t._e()]],2):t._e()])},r=[],i=(a("a1Th"),a("dRSK"),a("VRzm"),a("Kw5r")),s=a("LvDl"),o=a.n(s),c=a("wiDz"),u=a("MB/c"),l=a("UeuA"),d=a("vf3E"),f=a("0Bu0"),h={created:function(){this.isLoading||this.getCandles()},components:{spinner:u["a"],chart:l["a"],paperTradeSummary:f["a"],roundtrips:d["a"]},data:function(){return{candleFetch:"idle",candles:!1}},computed:{id:function(){return this.$route.params.id},gekkos:function(){return this.$store.state.gekkos},archivedGekkos:function(){return this.$store.state.archivedGekkos},data:function(){return!!this.gekkos&&(o.a.has(this.gekkos,this.id)?this.gekkos[this.id]:!!o.a.has(this.archivedGekkos,this.id)&&this.archivedGekkos[this.id])},config:function(){return o.a.get(this,"data.config")},latestEvents:function(){return o.a.get(this,"data.events.latest")},initialEvents:function(){return o.a.get(this,"data.events.initial")},trades:function(){return o.a.get(this,"data.events.tradeCompleted")||[]},roundtrips:function(){return o.a.get(this,"data.events.roundtrip")||[]},isLive:function(){return o.a.has(this.gekkos,this.id)},type:function(){return this.data.logType},isStratrunner:function(){return"watcher"!==this.type},isArchived:function(){return this.data.stopped},warmupRemaining:function(){if(!this.isStratrunner)return!1;if(this.isArchived)return!1;if(this.initialEvents.stratWarmupCompleted)return!1;if(!this.initialEvents.candle)return!1;var t=o.a.get(this.config,"tradingAdvisor.historySize");if(!t)return!1;var e=o.a.get(this.config,"tradingAdvisor.candleSize")*t;return humanizeDuration(moment(this.initialEvents.candle.start).add(e,"m").diff(moment()),{largest:2})},chartData:function(){return{candles:this.candles,trades:this.trades}},report:function(){return o.a.get(this.latestEvents,"performanceReport")},stratName:function(){if(this.data)return this.data.config.tradingAdvisor.method},stratParams:function(){if(!this.data)return"Loading...";var t=i["a"].util.extend({},this.data.config[this.stratName]);return delete t.__empty,o.a.isEmpty(t)?"No parameters":JSON.stringify(t,null,4)},isLoading:function(){return!this.data||(!o.a.get(this.data,"events.initial.candle")||!o.a.get(this.data,"events.latest.candle"))},watcher:function(){var t=this;if(!this.isStratrunner)return!1;var e=i["a"].util.extend({},this.data.config.watch);return o.a.find(this.gekkos,function(a){return a.id!==t.id&&o.a.isEqual(e,a.config.watch)})},hasLeechers:function(){var t=this;if(this.isStratrunner)return!1;var e=i["a"].util.extend({},this.data.config.watch);return o.a.find(this.gekkos,function(a){return a.id!==t.id&&o.a.isEqual(e,a.config.watch)})}},watch:{"data.events.latest.candle.start":function(){setTimeout(this.getCandles,o.a.random(100,2e3))}},methods:{round:function(t){return(+t).toFixed(5)},humanizeDuration:function(t,e){return window.humanizeDuration(t,e)},moment:function(t){function e(e){return t.apply(this,arguments)}return e.toString=function(){return t.toString()},e}(function(t){return moment.utc(t)}),fmt:function(t){return moment.utc(t).format("YYYY-MM-DD HH:mm")},getCandles:function(){var t=this;if(!this.isLoading&&"fetching"!==this.candleFetch){this.candleFetch="fetching";var e=this.data.events.latest.candle.start,a=this.data.events.initial.candle.start,n=1;"watcher"!==this.type&&(n=this.data.config.tradingAdvisor.candleSize);var r={watch:this.data.config.watch,daterange:{to:e,from:a},candleSize:n};setTimeout(function(){Object(c["b"])("getCandles",r,function(e,a){if(t.candleFetch="fetched",!a||a.error||!o.a.isArray(a))return console.log(a);t.candles=a.map(function(t){return t.start=moment.unix(t.start).utc().format(),t})})},o.a.random(150,2500))}},stopGekko:function(){if(this.hasLeechers)return alert("This Gekko is fetching market data for multiple stratrunners, stop these first.");confirm("Are you sure you want to stop this Gekko?")&&Object(c["b"])("stopGekko",{id:this.data.id},function(t,e){console.log("stopped gekko")})},deleteGekko:function(){var t=this;if(!this.isArchived)return alert("This Gekko is still running, stop it first!");confirm("Are you sure you want to delete this Gekko?")&&Object(c["b"])("deleteGekko",{id:this.data.id},function(e,a){t.$router.push({path:"/live-gekkos/"})})}}},m=h,v=(a("/Dpa"),a("KHd+")),p=Object(v["a"])(m,n,r,!1,null,null,null);e["a"]=p.exports},ngcj:function(t,e,a){"use strict";var n=a("EAJ1"),r=a.n(n);r.a},nzng:function(t,e,a){},pd3X:function(t,e,a){},pyMo:function(t,e,a){},q431:function(t,e,a){"use strict";var n=a("ydmZ"),r=a.n(n);r.a},qDq4:function(t,e,a){"use strict";var n=a("nzng"),r=a.n(n);r.a},r0f2:function(t,e,a){"use strict";a.d(e,"b",function(){return i}),a.d(e,"a",function(){return r});var n,r,i,s=window.CONFIG.ui,o="".concat(s.host).concat(80===s.port?"":":".concat(s.port)).concat(s.path);n=s.ssl?"https://".concat(o):"http://".concat(o),r=n+"api/",i=s.ssl?"wss://".concat(o,"api"):"ws://".concat(o,"api")},rloZ:function(t,e,a){"use strict";var n=function(){var t=this,e=t.$createElement,a=t._self._c||e;return a("div",{staticClass:"grd"},[a("div",{staticClass:"px1"},[a("h3",[t._v("Paper trader")]),"closed"===t.toggle?a("a",{staticClass:"btn--primary",attrs:{href:"#"},on:{click:function(e){return e.preventDefault(),t.switchToggle(e)}}},[t._v("Change paper trader settings")]):t._e(),"open"===t.toggle?[a("p",[t._v("Settings:")]),a("textarea",{directives:[{name:"model",rawName:"v-model",value:t.rawPaperTraderParams,expression:"rawPaperTraderParams"}],staticClass:"params",domProps:{value:t.rawPaperTraderParams},on:{input:function(e){e.target.composing||(t.rawPaperTraderParams=e.target.value)}}}),t.rawPaperTraderParamsError?a("p",{staticClass:"bg--red p1"},[t._v(t._s(t.rawPaperTraderParamsError.message))]):t._e()]:t._e()],2)])},r=[],i=(a("LvDl"),a("wiDz")),s={created:function(){var t=this;Object(i["a"])("configPart/paperTrader",function(e,a){t.rawPaperTraderParams=a.part})},data:function(){return{rawPaperTraderParams:"",rawPaperTraderParamsError:!1,paperTraderParams:{},toggle:"closed"}},watch:{rawPaperTraderParams:function(){this.emitConfig()}},methods:{switchToggle:function(){"open"===this.toggle?this.toggle="closed":this.toggle="open"},emitConfig:function(){this.parseParams(),this.$emit("settings",this.paperTraderParams)},parseParams:function(){try{this.paperTraderParams=toml.parse(this.rawPaperTraderParams),this.paperTraderParams.reportRoundtrips=!0,this.rawPaperTraderParamsError=!1}catch(t){this.rawPaperTraderParamsError=t,this.paperTraderParams={}}}}},o=s,c=(a("0zrD"),a("KHd+")),u=Object(c["a"])(o,n,r,!1,null,null,null);e["a"]=u.exports},spvI:function(t,e,a){"use strict";a("Vd3H"),a("rGqo");var n=a("wiDz"),r={data:function(){return{datasets:[],datasetScanstate:"idle",unscannableMakets:[]}},methods:{scan:function(){var t=this;this.datasetScanstate="scanning",Object(n["b"])("scansets",{},function(e,a){t.datasetScanstate="scanned",t.unscannableMakets=a.errors;var n=[];a.datasets.forEach(function(t){t.ranges.forEach(function(e,a){n.push({exchange:t.exchange,currency:t.currency,asset:t.asset,from:moment.unix(e.from).utc(),to:moment.unix(e.to).utc(),id:t.exchange+t.asset+t.currency+a})})}),n=n.filter(function(t){if(t.to.diff(t.from,"hours")>2)return!0}),n=n.sort(function(t,e){var a=t.to.diff(t.from),n=e.to.diff(e.from);return an?1:0}).reverse(),t.datasets=n})}}};e["a"]=r},tr8f:function(t,e,a){"use strict";var n=a("h8ks"),r=a.n(n);r.a},tr8z:function(t,e,a){"use strict";var n=a("Ug4+"),r=a.n(n);r.a},uMTv:function(t,e,a){"use strict";var n=a("pyMo"),r=a.n(n);r.a},uXxc:function(t,e,a){},vf3E:function(t,e,a){"use strict";var n=function(){var t=this,e=t.$createElement,a=t._self._c||e;return a("div",{staticClass:"contain roundtrips"},[a("h2",[t._v("Roundtrips")]),t.roundtrips.length?a("table",[a("thead",[t._m(0),t._l(t.roundtrips,function(e){return a("tr",[a("td",[t._v(t._s(t.fmt(e.entryAt)))]),a("td",[t._v(t._s(t.fmt(e.exitAt)))]),a("td",[t._v(t._s(t.diff(e.duration)))]),a("td",[t._v(t._s(t.round(e.entryBalance)))]),a("td",[t._v(t._s(t.round(e.exitBalance)))]),-1===Math.sign(e.pnl)?[a("td",{staticClass:"loss"},[t._v(t._s(Math.sign(e.pnl)*e.pnl.toFixed(2)))]),a("td",{staticClass:"loss"},[t._v(t._s(e.profit.toFixed(2))+"%")])]:[a("td",{staticClass:"profit"},[t._v(t._s(e.pnl.toFixed(2)))]),a("td",{staticClass:"profit"},[t._v(t._s(e.profit.toFixed(2))+"%")])]],2)})],2)]):t._e(),t.roundtrips.length?t._e():a("div",[a("p",[t._v("Not enough data to display")])])])},r=[function(){var t=this,e=t.$createElement,a=t._self._c||e;return a("tr",[a("th",[t._v("Entry at (UTC)")]),a("th",[t._v("Exit at (UTC)")]),a("th",[t._v("Exposure")]),a("th",[t._v("Entry balance")]),a("th",[t._v("Exit balance")]),a("th",[t._v("P&L")]),a("th",[t._v("Profit")])])}],i=a("LvDl"),s=a.n(i),o={props:["roundtrips"],data:function(){return{}},methods:{diff:function(t){return moment.duration(t).humanize()},humanizeDuration:function(t){return window.humanizeDuration(t)},fmt:function(t){var e;return e=s.a.isNumber(t)?moment.unix(t):moment(t).utc(),e.utc().format("YYYY-MM-DD HH:mm")},round:function(t){return(+t).toFixed(3)}}},c=o,u=(a("dW8q"),a("KHd+")),l=Object(u["a"])(c,n,r,!1,null,null,null);e["a"]=l.exports},vwRV:function(t,e,a){},w3yp:function(t,e,a){},wVP4:function(t,e,a){"use strict";var n=a("vwRV"),r=a.n(n);r.a},wVPO:function(t,e,a){"use strict";var n=a("uXxc"),r=a.n(n);r.a},wiDz:function(t,e,a){"use strict";a.d(e,"b",function(){return u}),a.d(e,"a",function(){return l});var n=a("24Ii"),r=a.n(n),i=a("TrxG"),s=a.n(i),o=a("r0f2"),c=function(t){return function(e,a){if(e)return t(e);if(!a.text)return t("no data");var n=JSON.parse(a.text);t(!1,n)}},u=function(t,e,a){r.a.post(o["a"]+t).use(s.a).send(e).end(c(a))},l=function(t,e){r.a.get(o["a"]+t).use(s.a).end(c(e))}},ydmZ:function(t,e,a){},yomC:function(t,e,a){"use strict";var n=a("EfWa"),r=a.n(n);r.a},yuKf:function(t,e,a){}}); +(function(t){function e(e){for(var n,s,o=e[0],c=e[1],u=e[2],d=0,f=[];d0?"profit":"loss"}}},s=i,o=(a("tr8z"),a("KHd+")),c=Object(o["a"])(s,n,r,!1,null,null,null);e["a"]=c.exports},"0zrD":function(t,e,a){"use strict";var n=a("jf14"),r=a.n(n);r.a},26:function(t,e,a){t.exports=a("Vtdi")},"2A8w":function(t,e,a){"use strict";var n=a("TDb6"),r=a.n(n);r.a},"2Yda":function(t,e,a){"use strict";var n=function(){var t=this,e=t.$createElement,a=t._self._c||e;return a("div",{staticClass:"contain my2"},[a("h3",[t._v("Start a new gekko")]),a("gekko-config-builder",{on:{config:t.updateConfig}}),a("div",{staticClass:"hr"}),t.config.valid?a("div",{staticClass:"txt--center"},[t.pendingStratrunner?t._e():a("a",{staticClass:"w100--s my1 btn--primary",attrs:{href:"#"},on:{click:function(e){return e.preventDefault(),t.start(e)}}},[t._v("Start")]),t.pendingStratrunner?a("spinner"):t._e()],1):t._e()],1)},r=[],i=(a("Z2Ku"),a("L9s1"),a("dRSK"),a("LvDl")),s=a.n(i),o=a("Kw5r"),c=a("wiDz"),u=function(){var t=this,e=t.$createElement,a=t._self._c||e;return a("div",{staticClass:"grd contain"},[a("div",{staticClass:"grd-row"},[a("div",{staticClass:"grd-row-col-3-6 mx1"},[a("h3",[t._v("Market")]),a("market-picker",{attrs:{"only-tradable":t.isTradebot},on:{market:t.updateMarketConfig}})],1),a("div",{staticClass:"grd-row-col-3-6 mx1"},[a("type-picker",{on:{type:t.updateType}})],1)]),"market watcher"!==t.type?[a("div",{staticClass:"hr"}),a("strat-picker",{staticClass:"contain my2",on:{stratConfig:t.updateStrat}}),"paper trader"===t.type?a("div",{staticClass:"hr"}):t._e(),"paper trader"===t.type?a("paper-trader",{on:{settings:t.updatePaperTrader}}):t._e()]:t._e()],2)},l=[],d=(a("91GP"),a("6BxS")),f=function(){var t=this,e=t.$createElement,a=t._self._c||e;return a("div",[a("h3",[t._v("Type")]),[a("label",{staticClass:"wrapper",attrs:{for:"type"}},[t._v("What do you want to do with gekko?")]),a("form",{staticClass:"radio grd"},t._l(t.types,function(e,n){return a("div",{staticClass:"grd-row m1"},[a("input",{directives:[{name:"model",rawName:"v-model",value:t.selectedTypeIndex,expression:"selectedTypeIndex"}],staticClass:"grd-row-col-1-6",attrs:{type:"radio"},domProps:{value:n,checked:t._q(t.selectedTypeIndex,n)},on:{change:function(e){t.selectedTypeIndex=n}}}),a("label",{staticClass:"grd-row-col-5-6",attrs:{for:n}},[t._v(t._s(e))])])}))]],2)},h=[],m={created:function(){this.emitType()},data:function(){return{types:["paper trader","market watcher","tradebot"],selectedTypeIndex:0}},methods:{emitType:function(){this.$emit("type",this.type)}},watch:{type:function(){this.emitType()}},computed:{type:function(){return this.types[this.selectedTypeIndex]}}},v=m,p=(a("wVPO"),a("KHd+")),g=Object(p["a"])(v,f,h,!1,null,null,null),_=g.exports,k=a("6Wkr"),y=a("rloZ"),w={created:function(){var t=this;Object(c["a"])("configPart/candleWriter",function(e,a){t.candleWriter=toml.parse(a.part)}),Object(c["a"])("configPart/performanceAnalyzer",function(e,a){t.performanceAnalyzer=toml.parse(a.part),t.performanceAnalyzer.enabled=!0})},data:function(){return{market:{},range:{},type:"",strat:{},paperTrader:{},candleWriter:{},performanceAnalyzer:{}}},components:{marketPicker:d["a"],typePicker:_,stratPicker:k["a"],paperTrader:y["a"]},computed:{isTradebot:function(){return"tradebot"===this.type},config:function(){var t={};return Object.assign(t,this.market,this.strat,{paperTrader:this.paperTrader},{candleWriter:this.candleWriter},{type:this.type},{performanceAnalyzer:this.performanceAnalyzer}),this.isTradebot&&(delete t.paperTrader,t.trader={enabled:!0}),t.valid=this.validConfig(t),t}},methods:{validConfig:function(t){if("market watcher"===t.type)return!0;if(!t.tradingAdvisor)return!1;if(s.a.isNaN(t.tradingAdvisor.candleSize))return!1;if(0==t.tradingAdvisor.candleSize)return!1;var e=t.tradingAdvisor.method;return!s.a.isEmpty(t[e])},updateMarketConfig:function(t){this.market=t,this.emitConfig()},updateType:function(t){this.type=t,this.emitConfig()},updateStrat:function(t){this.strat=t,this.emitConfig()},updatePaperTrader:function(t){this.paperTrader=t,this.paperTrader.enabled=!0,this.emitConfig()},emitConfig:function(){this.$emit("config",this.config)}}},b=w,C=(a("YEdZ"),Object(p["a"])(b,u,l,!1,null,null,null)),x=C.exports,S=a("MB/c"),T={components:{gekkoConfigBuilder:x,spinner:S["a"]},data:function(){return{pendingStratrunner:!1,config:{}}},computed:{gekkos:function(){return this.$store.state.gekkos},watchConfig:function(){var t=s.a.pick(this.config,"watch","candleWriter"),e=o["a"].util.extend({},t);return e.type="market watcher",e.mode="realtime",e},requiredHistoricalData:function(){if(this.config.tradingAdvisor&&this.config.valid){var t=this.config.tradingAdvisor;return t.candleSize*t.historySize}},gekkoConfig:function(){var t;if(this.existingMarketWatcher){if(this.requiredHistoricalData){var e=moment().utc().startOf("minute").subtract(this.requiredHistoricalData,"minutes").unix(),a=moment.utc(this.existingMarketWatcher.events.initial.candle.start).unix();t=moment.unix(Math.max(e,a)).utc().format()}else t=moment().utc().startOf("minute").format();var n=o["a"].util.extend({market:{type:"leech",from:t},mode:"realtime"},this.config);return n}},existingMarketWatcher:function(){var t=o["a"].util.extend({},this.watchConfig.watch);return s.a.find(this.gekkos,{config:{watch:t}})},exchange:function(){return this.watchConfig.watch.exchange},existingTradebot:function(){var t=this;return s.a.find(this.gekkos,function(e){return"tradebot"===e.logType&&e.config.watch.exchange===t.exchange})},availableApiKeys:function(){return this.$store.state.apiKeys}},watch:{existingMarketWatcher:function(t,e){var a=this;if(this.pendingStratrunner){var n=this.existingMarketWatcher;n.events.latest.candle&&(this.pendingStratrunner=!1,this.startGekko(function(t,e){a.$router.push({path:"/live-gekkos/".concat(e.id)})}))}}},methods:{updateConfig:function(t){this.config=t},start:function(){var t=this;if("tradebot"===this.config.type){if(this.existingTradebot){var e="You already have a tradebot running on this exchange";return e+=", you can only run one tradebot per exchange.",alert(e)}if(!this.availableApiKeys.includes(this.exchange))return alert("Please first configure API keys for this exchange in the config page.")}"market watcher"===this.config.type?this.existingMarketWatcher?(alert("This market is already being watched, redirecting you now..."),this.$router.push({path:"/live-gekkos/".concat(this.existingMarketWatcher.id)})):this.startWatcher(function(e,a){t.$router.push({path:"/live-gekkos/".concat(a.id)})}):this.existingMarketWatcher?this.startGekko(this.routeToGekko):this.startWatcher(function(e,a){t.pendingStratrunner=a.id})},routeToGekko:function(t,e){if(t||e.error)return console.error(t,e.error);this.$router.push({path:"/live-gekkos/".concat(e.id)})},startWatcher:function(t){Object(c["b"])("startGekko",this.watchConfig,t)},startGekko:function(t){Object(c["b"])("startGekko",this.gekkoConfig,t)}}},E=T,P=(a("2A8w"),Object(p["a"])(E,n,r,!1,null,null,null));e["a"]=P.exports},"2rY9":function(t,e,a){"use strict";var n=a("SWS5"),r=a.n(n);r.a},"5/bm":function(t,e,a){},"5shn":function(t,e,a){"use strict";var n=a("DlQD"),r=new n.Renderer;r.link=function(t,e,a){var n,r,i;return n=/^https?:\/\/.+$/.test(t),r=n||"newWindow"===e,i='"+a+""},n.setOptions({renderer:r}),e["a"]=n},"6BxS":function(t,e,a){"use strict";var n=function(){var t=this,e=t.$createElement,a=t._self._c||e;return a("div",[a("div",{staticClass:"mx1"},[a("label",{staticClass:"wrapper",attrs:{for:"exchange"}},[t._v("Exchange:")]),a("div",{staticClass:"custom-select button"},[a("select",{directives:[{name:"model",rawName:"v-model",value:t.exchange,expression:"exchange"}],on:{change:function(e){var a=Array.prototype.filter.call(e.target.options,function(t){return t.selected}).map(function(t){var e="_value"in t?t._value:t.value;return e});t.exchange=e.target.multiple?a:a[0]}}},t._l(t.exchanges,function(e,n){return a("option",[t._v(t._s(n))])}))])]),a("div",{staticClass:"grd-row"},[a("div",{staticClass:"grd-row-col-3-6 mx1"},[a("label",{attrs:{for:"currency"}},[t._v("Currency:")]),a("div",{staticClass:"custom-select button"},[a("select",{directives:[{name:"model",rawName:"v-model",value:t.currency,expression:"currency"}],on:{change:function(e){var a=Array.prototype.filter.call(e.target.options,function(t){return t.selected}).map(function(t){var e="_value"in t?t._value:t.value;return e});t.currency=e.target.multiple?a:a[0]}}},t._l(t.currencies,function(e){return a("option",[t._v(t._s(e))])}))])]),a("div",{staticClass:"grd-row-col-3-6 mx1"},[a("label",{attrs:{for:"asset"}},[t._v("Asset:")]),a("div",{staticClass:"custom-select button"},[a("select",{directives:[{name:"model",rawName:"v-model",value:t.asset,expression:"asset"}],on:{change:function(e){var a=Array.prototype.filter.call(e.target.options,function(t){return t.selected}).map(function(t){var e="_value"in t?t._value:t.value;return e});t.asset=e.target.multiple?a:a[0]}}},t._l(t.assets,function(e){return a("option",[t._v(t._s(e))])}))])])])])},r=[],i=(a("rGqo"),a("yt8O"),a("f3/d"),a("91GP"),a("LvDl")),s=a.n(i),o=(a("YIjs"),a("FhOJ"),a("wiDz"),{props:["onlyTradable","onlyImportable"],data:function(){return{exchange:"poloniex",currency:"USDT",asset:"BTC"}},created:function(){this.emitConfig()},computed:{exchanges:function(){var t=Object.assign({},this.$store.state.exchanges);return!s.a.isEmpty(t)&&(this.onlyTradable&&s.a.each(t,function(e,a){e.tradable||delete t[a]}),this.onlyImportable&&s.a.each(t,function(e,a){e.importable||delete t[a]}),t)},markets:function(){return this.exchanges?this.exchanges[this.exchange]:null},assets:function(){return this.exchanges?this.exchanges[this.exchange].markets[this.currency]:null},currencies:function(){return this.exchanges?s.a.keys(this.exchanges[this.exchange].markets):null},watchConfig:function(){return{watch:{exchange:this.exchange,currency:this.currency,asset:this.asset}}}},watch:{currency:function(){this.emitConfig()},asset:function(){this.emitConfig()},market:function(){this.emitConfig()},exchanges:function(){this.emitConfig()},exchange:function(){this.emitConfig()}},methods:{emitConfig:function(){this.$emit("market",this.watchConfig)}}}),c=o,u=a("KHd+"),l=Object(u["a"])(c,n,r,!1,null,null,null);e["a"]=l.exports},"6Wkr":function(t,e,a){"use strict";var n=function(){var t=this,e=t.$createElement,a=t._self._c||e;return a("div",{staticClass:"grd"},[a("div",{staticClass:"grd-row"},[a("div",{staticClass:"grd-row-col-3-6 px1"},[a("h3",[t._v("Strategy")]),a("div",[a("label",{staticClass:"wrapper",attrs:{for:"strat"}},[t._v("Strategy:")]),a("div",{staticClass:"custom-select button"},[a("select",{directives:[{name:"model",rawName:"v-model",value:t.strategy,expression:"strategy"}],on:{change:function(e){var a=Array.prototype.filter.call(e.target.options,function(t){return t.selected}).map(function(t){var e="_value"in t?t._value:t.value;return e});t.strategy=e.target.multiple?a:a[0]}}},t._l(t.strategies,function(e){return a("option",[t._v(t._s(e.name))])}))])]),a("div",[a("label",{attrs:{for:"candleSize"}},[t._v("Candle Size")]),a("div",{staticClass:"grd-row"},[a("div",{staticClass:"grd-row-col-3-6"},[a("input",{directives:[{name:"model",rawName:"v-model",value:t.rawCandleSize,expression:"rawCandleSize"}],domProps:{value:t.rawCandleSize},on:{input:function(e){e.target.composing||(t.rawCandleSize=e.target.value)}}})]),a("div",{staticClass:"grd-row-col-3-6 align"},[a("div",{staticClass:"custom-select button"},[a("select",{directives:[{name:"model",rawName:"v-model",value:t.candleSizeUnit,expression:"candleSizeUnit"}],on:{change:function(e){var a=Array.prototype.filter.call(e.target.options,function(t){return t.selected}).map(function(t){var e="_value"in t?t._value:t.value;return e});t.candleSizeUnit=e.target.multiple?a:a[0]}}},[a("option",[t._v("minutes")]),a("option",[t._v("hours")]),a("option",[t._v("days")])])])])])]),a("div",[a("label",{attrs:{for:"historySize"}},[t._v("Warmup period (in "+t._s(t.rawCandleSize)+" "+t._s(t.singularCandleSizeUnit)+" candles):")]),a("input",{directives:[{name:"model",rawName:"v-model",value:t.historySize,expression:"historySize"}],domProps:{value:t.historySize},on:{input:function(e){e.target.composing||(t.historySize=e.target.value)}}}),a("em",{staticClass:"label-like"},[t._v("(will use "+t._s(t.humanizeDuration(t.candleSize*t.historySize*1e3*60))+" of data as history)")])])]),a("div",{staticClass:"grd-row-col-3-6 px1"},[a("div",[a("h3",[t._v("Parameters")]),a("p",[t._v(t._s(t.strategy)+" Parameters:")]),a("textarea",{directives:[{name:"model",rawName:"v-model",value:t.rawStratParams,expression:"rawStratParams"}],staticClass:"params",domProps:{value:t.rawStratParams},on:{input:function(e){e.target.composing||(t.rawStratParams=e.target.value)}}}),t.rawStratParamsError?a("p",{staticClass:"bg--red p1"},[t._v(t._s(t.rawStratParamsError.message))]):t._e()])])])])},r=[],i=(a("dRSK"),a("LvDl")),s=a.n(i),o=a("wiDz"),c={data:function(){return{strategies:[],candleSizeUnit:"hours",rawCandleSize:1,strategy:"MACD",historySize:10,rawStratParams:"",rawStratParamsError:!1,emptyStrat:!1,stratParams:{}}},created:function(){var t=this;Object(o["a"])("strategies",function(e,a){t.strategies=a,s.a.each(t.strategies,function(t){t.empty=""===t.params}),t.rawStratParams=s.a.find(t.strategies,{name:t.strategy}).params,t.emptyStrat=s.a.find(t.strategies,{name:t.strategy}).empty,t.emitConfig()})},watch:{strategy:function(t){t=s.a.find(this.strategies,{name:t}),this.rawStratParams=t.params,this.emptyStrat=t.empty,this.emitConfig()},candleSize:function(){this.emitConfig()},historySize:function(){this.emitConfig()},rawStratParams:function(){this.emitConfig()}},computed:{candleSize:function(){return"minutes"===this.candleSizeUnit?this.rawCandleSize:"hours"===this.candleSizeUnit?60*this.rawCandleSize:"days"===this.candleSizeUnit?60*this.rawCandleSize*24:void 0},singularCandleSizeUnit:function(){return this.candleSizeUnit.slice(0,-1)},config:function(){var t={tradingAdvisor:{enabled:!0,method:this.strategy,candleSize:+this.candleSize,historySize:+this.historySize}};return this.emptyStrat?t[this.strategy]={__empty:!0}:t[this.strategy]=this.stratParams,t}},methods:{humanizeDuration:function(t){return window.humanizeDuration(t)},emitConfig:function(){this.parseParams(),this.$emit("stratConfig",this.config)},parseParams:function(){try{this.stratParams=toml.parse(this.rawStratParams),this.rawStratParamsError=!1}catch(t){this.rawStratParamsError=t,this.stratParams={}}}}},u=c,l=(a("tr8f"),a("KHd+")),d=Object(l["a"])(u,n,r,!1,null,null,null);e["a"]=d.exports},"7LpK":function(t,e,a){},"8KAT":function(t,e,a){},"9RND":function(t){t.exports={name:"gekko",version:"0.6.5",description:"A bitcoin trading bot for auto trading at various exchanges",keywords:["trading","bot","bitcoin","TA","finance"],scripts:{test:"./node_modules/.bin/mocha test/*.js --recursive test -u tdd --reporter spec",start:"node ./gekko --config config.js --ui"},author:"Mike van Rossum ",dependencies:{"@slack/client":"^3.10.0",async:"2.1.2","bitfinex-api-node":"^1.2.1","co-fs":"^1.2.0",commander:"^2.13.0",gekko:"0.0.9","humanize-duration":"^3.10.0",koa:"^1.2.0","koa-bodyparser":"^2.2.0","koa-cors":"0.0.16","koa-logger":"^1.3.0","koa-router":"^5.4.0","koa-static":"^2.0.0",lodash:"2.x",moment:"^2.20.1",opn:"^4.0.2","promisify-node":"^0.5.0","prompt-lite":"0.1.1",pushbullet:"1.4.3",relieve:"^2.1.3",retry:"^0.10.1",semver:"5.4.1",sqlite3:"^4.0.0","stats-lite":"^2.0.4","tiny-promisify":"^0.1.1",toml:"^2.3.0",twitter:"^1.7.1"},devDependencies:{chai:"^4.1.2",mocha:"^5.0.0",proxyquire:"^1.7.10",request:"^2.83.0","request-promise":"^4.2.2",sinon:"^4.2.0"},engines:{node:">=8.11.2"},license:"MIT",repository:{type:"git",url:"https://github.com/askmike/gekko.git"}}},CyGp:function(t,e,a){"use strict";var n=a("jTvs"),r=a.n(n);r.a},EAJ1:function(t,e,a){},EDI0:function(t,e,a){},EfWa:function(t,e,a){},FhOJ:function(t,e,a){"use strict";var n=function(){var t=this,e=t.$createElement,a=t._self._c||e;return a("div",[a("h3",[t._v("Daterange")]),a("div",[a("label",{attrs:{for:"from"}},[t._v("From")]),a("input",{directives:[{name:"model",rawName:"v-model",value:t.from,expression:"from"}],domProps:{value:t.from},on:{input:function(e){e.target.composing||(t.from=e.target.value)}}})]),a("div",[a("label",{attrs:{for:"to"}},[t._v("To")]),a("input",{directives:[{name:"model",rawName:"v-model",value:t.to,expression:"to"}],domProps:{value:t.to},on:{input:function(e){e.target.composing||(t.to=e.target.value)}}})])])},r=[],i=(a("LvDl"),a("wiDz"),{data:function(){return{from:"",to:""}},created:function(){var t=moment().startOf("minute"),e=t.clone().subtract(3,"months");this.to=this.fmt(t),this.from=this.fmt(e),this.emitRange()},methods:{fmtTs:function(t){return moment.unix(t).utc()},fmt:function(t){return t.utc().format("YYYY-MM-DD HH:mm")},emitRange:function(){this.$emit("range",{from:this.fmtTs(this.from),to:this.fmtTs(this.to)})},emitManualEntry:function(){if(this.from.length<"4"||this.from.length<"4")return this.$emit("range",{});var t=moment.utc(this.from),e=moment.utc(this.to);t.isValid()&&e.isValid()?this.$emit("range",{from:this.fmt(t),to:this.fmt(e)}):this.$emit("range",{})}},watch:{from:function(){this.emitManualEntry()},to:function(){this.emitManualEntry()},config:function(){this.scanned=!1},tab:function(){this.scanned=!1,this.$emit("range",{})},selectedRangeIndex:function(){var t=this.ranges[this.selectedRangeIndex];t&&this.emitRange(t)}}}),s=i,o=(a("TPp/"),a("KHd+")),c=Object(o["a"])(s,n,r,!1,null,null,null);e["a"]=c.exports},"H+ir":function(t,e,a){"use strict";var n=a("5/bm"),r=a.n(n);r.a},Kd0R:function(t,e,a){},"MB/c":function(t,e,a){"use strict";var n=function(){var t=this,e=t.$createElement;t._self._c;return t._m(0)},r=[function(){var t=this,e=t.$createElement,a=t._self._c||e;return a("div",{staticClass:"spinner"},[a("div",{staticClass:"rect1"}),a("div",{staticClass:"rect2"}),a("div",{staticClass:"rect3"}),a("div",{staticClass:"rect4"})])}],i={},s=i,o=(a("q431"),a("KHd+")),c=Object(o["a"])(s,n,r,!1,null,null,null);e["a"]=c.exports},Pf3K:function(t,e,a){"use strict";var n=function(){var t=this,e=t.$createElement,a=t._self._c||e;return a("div",{attrs:{id:"app"}},[a("top"),a("div",{staticClass:"fill"},[a("router-view",{staticClass:"view"})],1),a("bottom"),a("modal")],1)},r=[],i=function(){var t=this,e=t.$createElement,a=t._self._c||e;return a("div",[a("div",{attrs:{id:"top"}}),t._m(0),a("nav",{staticClass:"bg--light-gray"},[a("div",{staticClass:"menu contain"},[a("router-link",{staticClass:"py1",attrs:{to:"/home"}},[t._v("Home")]),a("router-link",{staticClass:"py1",attrs:{to:"/live-gekkos"}},[t._v("Live Gekkos")]),a("router-link",{staticClass:"py1",attrs:{to:"/backtest"}},[t._v("Backtest")]),a("router-link",{staticClass:"py1",attrs:{to:"/data"}},[t._v("Local data")]),a("router-link",{staticClass:"py1",attrs:{to:"/config"}},[t._v("Config")]),a("a",{staticClass:"py1",attrs:{href:"https://gekko.wizb.it/docs/introduction/about_gekko.html",target:"_blank"}},[t._v("Documentation")])],1)])])},s=[function(){var t=this,e=t.$createElement,a=t._self._c||e;return a("header",{staticClass:"bg--off-white grd"},[a("div",{staticClass:"contain grd-row"},[a("h3",{staticClass:"py1 px2 col-2"},[t._v("Gekko UI")])])])}],o={},c=o,u=(a("uMTv"),a("KHd+")),l=Object(u["a"])(c,i,s,!1,null,null,null),d=l.exports,f=function(){var t=this,e=t.$createElement,a=t._self._c||e;return a("footer",{staticClass:"p2 bg--off-white"},[a("div",{staticClass:"contain"},[t._m(0),a("p",[t._v("Using Gekko v"+t._s(t.version.gekko)+" and Gekko UI v"+t._s(t.version.ui)+".")])])])},h=[function(){var t=this,e=t.$createElement,a=t._self._c||e;return a("p",[a("em",[t._v("Use Gekko at your own risk.")])])}],m=a("9RND"),v=a("kiQV"),p={data:function(){return{version:{gekko:m.version,ui:v.version}}}},g=p,_=Object(u["a"])(g,f,h,!1,null,null,null),k=_.exports,y=function(){var t=this,e=t.$createElement,a=t._self._c||e;return t.active?a("div",[a("div",{attrs:{id:"modal-background"}}),a("div",{staticClass:"modal",attrs:{id:"modal"}},[a("div",{staticClass:"modal-guts",domProps:{innerHTML:t._s(t.content)}})])]):t._e()},w=[],b=a("5shn"),C={disconnected:Object(b["a"])("\n\n## Disconnected\n\nSomething happened to either Gekko or the connection.\nPlease check the terminal where Gekko is running or\nyour network connection.\n\n*This message is shown when the UI is unable to open a websocket connection with the Gekko Server.*\n\n ")},x={computed:{active:function(){return!this.$store.state.warnings.connected},content:function(){return this.$store.state.warnings.connected?"":C.disconnected}}},S=x,T=(a("TfKe"),Object(u["a"])(S,y,w,!1,null,null,null)),E=T.exports,P={name:"app",components:{top:d,bottom:k,modal:E}},A=P,z=(a("ZL7j"),Object(u["a"])(A,n,r,!1,null,null,null));e["a"]=z.exports},Q2AE:function(t,e,a){"use strict";var n={};a.r(n),a.d(n,"addImport",function(){return d}),a.d(n,"syncImports",function(){return f}),a.d(n,"updateImport",function(){return h});var r={};a.r(r),a.d(r,"syncGekkos",function(){return g}),a.d(r,"addGekko",function(){return _}),a.d(r,"updateGekko",function(){return k}),a.d(r,"archiveGekko",function(){return y}),a.d(r,"errorGekko",function(){return w}),a.d(r,"deleteGekko",function(){return b});var i={};a.r(i),a.d(i,"setGlobalWarning",function(){return C});var s={};a.r(s),a.d(s,"syncApiKeys",function(){return x}),a.d(s,"syncExchanges",function(){return S});a("VRzm");var o=a("Kw5r"),c=a("L2JU"),u=a("LvDl"),l=a.n(u),d=(a("INYr"),function(t,e){return t.imports.push(e),t}),f=function(t,e){return t.imports=e,t},h=function(t,e){var a=t.imports.findIndex(function(t){return t.id===e.import_id}),n=t.imports[a];if(!n)return t;var r=o["a"].util.extend(n,e.updates);return o["a"].set(t.imports,a,r),t},m=a("oyJW"),v=a("yT7P"),p=a("c7Wa"),g=function(t,e){return e?(t.gekkos=e.live,t.archivedGekkos=e.archive,t):t},_=function(t,e){return t.gekkos=Object(v["a"])({},t.gekkos,Object(m["a"])({},e.id,e)),t},k=function(t,e){return e.id&&l.a.has(t.gekkos,e.id)?(t.gekkos=Object(v["a"])({},t.gekkos,Object(m["a"])({},e.id,p(t.gekkos[e.id],e.event))),t):console.error("cannot update unknown gekko..")},y=function(t,e){return l.a.has(t.gekkos,e)?(t.archivedGekkos=Object(v["a"])({},t.archivedGekkos,Object(m["a"])({},e,Object(v["a"])({},t.gekkos[e],{stopped:!0,active:!1}))),t.gekkos=l.a.omit(t.gekkos,e),t):console.error("cannot archive unknown gekko..")},w=function(t,e){return l.a.has(t.gekkos,e.id)?(t.gekkos=Object(v["a"])({},t.gekkos,Object(m["a"])({},e.id,Object(v["a"])({},t.gekkos[e.id],{errored:!0,errorMessage:e.error}))),t):console.error("cannot error unknown gekko..")},b=function(t,e){return l.a.has(t.archivedGekkos,e)?(t.archivedGekkos=l.a.omit(t.archivedGekkos,e),t):console.error("cannot delete unknown gekko..")},C=function(t,e){return t.warnings[e.key]=e.value,t},x=function(t,e){return o["a"].set(t,"apiKeys",e),t},S=function(t,e){return o["a"].set(t,"exchanges",e),t};o["a"].use(c["a"]);var T=!1,E={};l.a.merge(E,n),l.a.merge(E,r),l.a.merge(E,i),l.a.merge(E,s);e["a"]=new c["a"].Store({state:{warnings:{connected:!0},imports:[],gekkos:{},archivedGekkos:{},connection:{disconnected:!1,reconnected:!1},apiKeys:[],exchanges:{}},mutations:E,strict:T})},Q6eY:function(t,e,a){"use strict";var n=a("SDwi"),r=a.n(n);r.a},SDwi:function(t,e,a){},SWS5:function(t,e,a){},T0Mt:function(t,e,a){"use strict";var n=a("pd3X"),r=a.n(n);r.a},TDb6:function(t,e,a){},"TPp/":function(t,e,a){"use strict";var n=a("7LpK"),r=a.n(n);r.a},TfKe:function(t,e,a){"use strict";var n=a("ZpQ2"),r=a.n(n);r.a},UeuA:function(t,e,a){"use strict";var n=function(){var t=this,e=t.$createElement,a=t._self._c||e;return a("div",{class:{clickable:!t.isClicked},attrs:{id:"chartWrapper"}},[a("div",{staticClass:"shield",on:{click:function(e){return e.preventDefault(),t.click(e)}}}),a("svg",{attrs:{id:"chart",width:"960",height:t.height}})])},r=[],i=a("k5N+"),s=a("LvDl"),o=a.n(s),c=function(t,e,a){var n=function(t){return o.a.isNumber(t)?moment.unix(t).utc().toDate():moment.utc(t).toDate()},r=e.map(function(t){return{price:t.price,date:n(t.date),action:t.action}}),s=t.map(function(t){return{price:t.open,date:n(t.start)}}),c=s.map(function(t){return+t.date}),u=s.map(function(t){return+t.price}),l=d3.select("#chart");l.attr("width",window.innerWidth-20);var d={top:20,right:20,bottom:110,left:40},f=a-d.top-d.bottom,h={top:a-70,right:20,bottom:30,left:40},m=+l.attr("width")-d.left-d.right,v=a-h.top-h.bottom,p=d3.scaleUtc().range([0,m]),g=d3.scaleUtc().range([0,m]),_=d3.scaleLinear().range([f,0]),k=d3.scaleLinear().range([v,0]),y=d3.axisBottom(p),w=d3.axisBottom(g),b=d3.axisLeft(_).ticks(a/50),C=d3.brushX().extent([[0,0],[m,v]]).on("brush end",z),x=d3.zoom().scaleExtent([1,100]).translateExtent([[0,0],[m,f]]).extent([[0,0],[m,f]]).on("zoom",$),S=d3.line().x(function(t){return p(t.date)}).y(function(t){return _(t.price)}),T=d3.line().x(function(t){return g(t.date)}).y(function(t){return k(t.price)});l.append("defs").append("clipPath").attr("id","clip").append("rect").attr("width",m).attr("height",f);var E=l.append("g").attr("class","focus").attr("transform","translate("+d.left+","+d.top+")"),P=l.append("g").attr("class","context").attr("transform","translate("+h.left+","+h.top+")");p.domain(d3.extent(s,function(t){return t.date})),_.domain([.99*d3.min(u),1.01*d3.max(u)]),g.domain(p.domain()),k.domain(_.domain()),E.append("path").datum(s).attr("class","line price").attr("d",S),E.append("g").attr("class","axis axis--x").attr("transform","translate(0,"+f+")").call(y),E.append("g").attr("class","axis axis--y").call(b),P.append("path").datum(s).attr("class","line").attr("d",T),P.append("g").attr("class","axis axis--x").attr("transform","translate(0,"+v+")").call(w);var A=l.append("g").attr("transform","translate("+d.left+","+d.top+")").selectAll("circle").data(r).enter().append("circle").attr("class",function(t){return t.action}).attr("cx",function(t){return p(t.date)}).attr("cy",function(t){return _(t.price)}).attr("r",5);P.append("g").selectAll("circle").data(r).enter().append("circle").attr("class",function(t){return t.action}).attr("cx",function(t){return g(t.date)}).attr("cy",function(t){return k(t.price)}).attr("r",3);function z(){if(!d3.event.sourceEvent||"zoom"!==d3.event.sourceEvent.type){var t=d3.event.selection||g.range();p.domain(t.map(g.invert,g)),D(p.domain()),l.select(".axis--y").call(b),A.attr("cx",function(t){return p(t.date)}).attr("cy",function(t){return _(t.price)}),E.select(".line").attr("d",S),E.select(".axis--x").call(y),l.select(".zoom").call(x.transform,d3.zoomIdentity.scale(m/(t[1]-t[0])).translate(-t[0],0))}}function D(t){var e=Object(i["a"])(t,2),a=e[0],n=e[1],r=o.a.sortedIndex(c,a),s=o.a.sortedIndex(c,n),l=u.slice(r,s);_.domain([.9995*d3.min(l),1.0005*d3.max(l)])}function $(){if(!d3.event.sourceEvent||"brush"!==d3.event.sourceEvent.type){var t=d3.event.transform;D(t.rescaleX(g).domain()),l.select(".axis--y").call(b),p.domain(t.rescaleX(g).domain()),E.select(".line").attr("d",S),A.attr("cx",function(t){return p(t.date)}).attr("cy",function(t){return _(t.price)}),E.select(".axis--x").call(y),P.select(".brush").call(C.move,p.range().map(t.invertX,t))}}P.append("g").attr("class","brush").call(C).call(C.move,p.range()),l.append("rect").attr("class","zoom").attr("width",m).attr("height",f).attr("transform","translate("+d.left+","+d.top+")").call(x)},u=(a("dRSK"),function(t){d3.select("#chart").append("text").attr("class","message").attr("x",150).attr("y",150).text(t)}),l=4,d={props:["data","height"],data:function(){return{isClicked:!1}},watch:{data:function(){this.render()}},created:function(){setTimeout(this.render,100)},beforeDestroy:function(){this.remove()},methods:{click:function(){this.isClicked=!0},render:function(){this.remove(),_.size(this.data.candles)0?"profit":"loss"}}},$=D,j=(a("WlXR"),Object(g["a"])($,P,A,!1,null,null,null)),O=j.exports,M=a("UeuA"),G=a("vf3E"),I={props:["result"],data:function(){return{}},methods:{},components:{roundtripTable:G["a"],resultSummary:O,chart:M["a"]},computed:{candles:function(){return{candles:this.result.stratCandles,trades:this.result.trades}}}},R=I,H=(a("wVP4"),Object(g["a"])(R,T,E,!1,null,null,null)),K=H.exports,Y={data:function(){return{backtestable:!1,backtestState:"idle",backtestResult:!1,config:!1}},methods:{check:function(t){if(this.config=t,!t.valid)return this.backtestable=!1;this.backtestable=!0},run:function(){var t=this;this.backtestState="fetching",Object(f["b"])("backtest",this.config,function(e,a){t.backtestState="fetched",t.backtestResult=a})}},components:{configBuilder:S,result:K,spinner:h["a"]}},W=Y,N=Object(g["a"])(W,n,r,!1,null,null,null);e["a"]=N.exports},c7Wa:function(t,e){const a=["marketUpdate"],n=["marketStart","stratWarmupCompleted"],r=["tradeCompleted","advice","roundtrip"],i=(t,e)=>{const i=e.type,s=e.payload;return t={...t,latestUpdate:new Date},r.includes(i)&&(t=t.events[i]?{...t,events:{...t.events,[i]:[...t.events[i],s]}}:{...t,events:{...t.events,[i]:[s]}}),t.events.initial[i]||a.includes(i)||(t={...t,events:{...t.events,initial:{...t.events.initial,[i]:s}}}),n.includes(i)||(t={...t,events:{...t.events,latest:{...t.events.latest,[i]:s}}}),t};t.exports=i},dHeD:function(t,e,a){"use strict";var n=function(){var t=this,e=t.$createElement,a=t._self._c||e;return a("div",{staticClass:"contain"},[a("div",{staticClass:"text",domProps:{innerHTML:t._s(t.intro)}}),a("div",{staticClass:"hr"}),a("h2",[t._v("Available datasets")]),"idle"===t.datasetScanstate?a("div",{staticClass:"txt--center my2"},[a("a",{staticClass:"w100--s btn--primary scan-btn",attrs:{href:"#"},on:{click:function(e){return e.preventDefault(),t.scan(e)}}},[t._v("Scan available data")])]):t._e(),"scanning"===t.datasetScanstate?a("div",{staticClass:"txt--center my2"},[a("spinner")],1):t._e(),"scanned"===t.datasetScanstate?a("div",{staticClass:"my2"},[t.unscannableMakets.length?a("div",{staticClass:"bg--orange p1 warning my1"},[t.viewUnscannable?t._e():a("p",{staticClass:"clickable",on:{click:function(e){return e.preventDefault(),t.toggleUnscannable(e)}}},[t._v("Some markets were unscannable, click here for details.")]),t.viewUnscannable?[a("p",[t._v("Unable to find datasets in the following markets:")]),t._l(t.unscannableMakets,function(e){return a("div",{staticClass:"mx2"},[t._v("- "+t._s(e.exchange)+":"+t._s(e.currency)+":"+t._s(e.asset))])})]:t._e()],2):t._e(),t.datasets.length?[a("table",{staticClass:"full data"},[t._m(0),a("tbody",t._l(t.datasets,function(e){return a("tr",[a("td",[t._v(t._s(e.exchange))]),a("td",[t._v(t._s(e.currency))]),a("td",[t._v(t._s(e.asset))]),a("td",[t._v(t._s(t.fmt(e.from)))]),a("td",[t._v(t._s(t.fmt(e.to)))]),a("td",[t._v(t._s(t.humanizeDuration(e.to.diff(e.from))))])])}))])]:t._e(),t.datasets.length?t._e():[a("p",[t._v("It looks like you don't have any local data yet.")])]],2):t._e(),a("div",{staticClass:"my2"},[a("h2",[t._v("Import more data")]),a("p",{staticClass:"text"},[t._v("You can easily import more market data directly from exchanges using the importer.")]),a("router-link",{staticClass:"btn--primary",attrs:{to:"/data/importer"}},[t._v("Go to the importer!")])],1)])},r=[function(){var t=this,e=t.$createElement,a=t._self._c||e;return a("thead",[a("tr",[a("th",[t._v("exchange")]),a("th",[t._v("currency")]),a("th",[t._v("asset")]),a("th",[t._v("from")]),a("th",[t._v("to")]),a("th",[t._v("duration")])])])}],i=(a("VRzm"),a("MB/c")),s=a("5shn"),o=a("spvI"),c=Object(s["a"])("\n\n## Local data\n\nGekko needs local market data in order to backtest strategies. The local\ndata can also be used in a warmup period when running a strategy against a\nlive market.\n\n"),u={mixins:[o["a"]],components:{spinner:i["a"]},data:function(){return{intro:c,viewUnscannable:!1}},methods:{toggleUnscannable:function(){this.viewUnscannable=!0},humanizeDuration:function(t){return window.humanizeDuration(t)},fmt:function(t){return t.format("YYYY-MM-DD HH:mm")}}},l=u,d=(a("2rY9"),a("KHd+")),f=Object(d["a"])(l,n,r,!1,null,null,null);e["a"]=f.exports},dW8q:function(t,e,a){"use strict";var n=a("w3yp"),r=a.n(n);r.a},daul:function(t,e,a){"use strict";var n=a("Kd0R"),r=a.n(n);r.a},g298:function(t,e,a){"use strict";var n=function(){var t=this,e=t.$createElement,a=t._self._c||e;return a("div",{staticClass:"contain my2"},[t.data&&!t.data.done?a("div",[a("h2",[t._v("Importing data..")]),a("div",{staticClass:"grd"},[a("div",{staticClass:"grd-row"},[t._m(0),a("div",{staticClass:"grd-row-col-4-6"},[t._v(t._s(t.data.watch.exchange))])]),a("div",{staticClass:"grd-row"},[t._m(1),a("div",{staticClass:"grd-row-col-4-6"},[t._v(t._s(t.data.watch.currency)+"/"+t._s(t.data.watch.asset))])])]),a("div",{staticClass:"grd"},[a("div",{staticClass:"grd-row"},[t._m(2),a("div",{staticClass:"grd-row-col-4-6"},[t._v(t._s(t.fmt(t.from)))])]),a("div",{staticClass:"grd-row"},[t._m(3),a("div",{staticClass:"grd-row-col-4-6"},[t._v(t._s(t.fmt(t.to)))])]),t.initialized?a("div",{staticClass:"grd-row"},[t._m(4),a("div",{staticClass:"grd-row-col-4-6"},[t._v(t._s(t.fmt(t.latest)))])]):t._e(),t.initialized?a("div",{staticClass:"grd-row"},[t._m(5),a("div",{staticClass:"grd-row-col-4-6"},[t._v(t._s(t.fromEnd))])]):t._e()]),t.initialized?t._e():a("spinner"),t.initialized?a("div",{staticClass:"contain"},[a("progressBar",{attrs:{progress:t.progress}})],1):t._e(),a("p",[a("em",[t._v("(you don't have to wait until the import is done,you can already start "),a("router-link",{attrs:{to:"/backtest"}},[t._v("backtesting")]),t._v(").")],1)])],1):t._e(),t.data&&t.data.done?a("div",{staticClass:"txt--center"},[a("h2",[t._v("Import done")]),a("p",[t._v(" Go and "),a("router-link",{attrs:{to:"/backtest"}},[t._v("backtest")]),t._v(" with your new data!")],1)]):t._e(),t.data?t._e():a("div",{staticClass:"txt--center"},[a("h2",[t._v("ERROR: Unknown import")]),a("p",[a("I",[t._v("don't know this import..")])],1)])])},r=[function(){var t=this,e=t.$createElement,a=t._self._c||e;return a("div",{staticClass:"grd-row-col-2-6"},[a("strong",[t._v("Market:")])])},function(){var t=this,e=t.$createElement,a=t._self._c||e;return a("div",{staticClass:"grd-row-col-2-6"},[a("strong",[t._v("Currency/Asset:")])])},function(){var t=this,e=t.$createElement,a=t._self._c||e;return a("div",{staticClass:"grd-row-col-2-6"},[a("strong",[t._v("From:")])])},function(){var t=this,e=t.$createElement,a=t._self._c||e;return a("div",{staticClass:"grd-row-col-2-6"},[a("strong",[t._v("To:")])])},function(){var t=this,e=t.$createElement,a=t._self._c||e;return a("div",{staticClass:"grd-row-col-2-6"},[a("strong",[t._v("Imported data until:")])])},function(){var t=this,e=t.$createElement,a=t._self._c||e;return a("div",{staticClass:"grd-row-col-2-6"},[a("strong",[t._v("Remaining:")])])}],i=(a("dRSK"),a("LvDl")),s=a.n(i),o=function(){var t=this,e=t.$createElement,a=t._self._c||e;return t.progress?a("div",{staticClass:"progressbarWrapper"},[a("p",[a("strong",[t._v(t._s(t.round(t.progress))+"%")])]),a("div",{staticClass:"progressbar"},[a("div",{style:{width:t.progress+"%"}})])]):t._e()},c=[],u={props:["progress"],methods:{round:function(t){return(+t).toFixed(2)}}},l=u,d=(a("/vJE"),a("KHd+")),f=Object(d["a"])(l,o,c,!1,null,null,null),h=f.exports,m=a("MB/c"),v={components:{progressBar:h,spinner:m["a"]},computed:{data:function(){return s.a.find(this.$store.state.imports,{id:this.$route.params.id})},initialized:function(){if(this.data&&this.latest.isValid())return!0},latest:function(){if(this.data)return this.mom(this.data.latest)},fromEndMs:function(){if(this.data)return this.to.diff(this.latest)},fromEnd:function(){return this.latest?humanizeDuration(this.fromEndMs):"LOADING"},from:function(){if(this.data)return this.mom(this.data.from)},to:function(){if(this.data)return this.mom(this.data.to)},timespan:function(){if(this.data)return this.to.diff(this.from)},progress:function(){if(this.data){var t=this.timespan-this.fromEndMs;return 100*t/this.timespan}}},methods:{fmt:function(t){return t.format("YYYY-MM-DD HH:mm:ss")},mom:function(t){return moment.utc(t)}}},p=v,g=(a("qDq4"),Object(d["a"])(p,n,r,!1,null,null,null));e["a"]=g.exports},gYof:function(t,e,a){"use strict";a("LvDl");var n=a("Kw5r"),r=a("r0f2"),i=(a("L2JU"),a("wiDz")),s=a("Q2AE"),o=function(){Object(i["a"])("imports",function(t,e){s["a"].commit("syncImports",e)})},c=function(){C.$on("import_update",function(t){s["a"].commit("updateImport",t)})},u=function(){o(),c()},l=function(){Object(i["a"])("gekkos",function(t,e){var a=e;s["a"].commit("syncGekkos",a)})},d=function(){C.$on("gekko_new",function(t){return s["a"].commit("addGekko",t.state)}),C.$on("gekko_event",function(t){return s["a"].commit("updateGekko",t)}),C.$on("gekko_archived",function(t){return s["a"].commit("archiveGekko",t.id)}),C.$on("gekko_error",function(t){return s["a"].commit("errorGekko",t)}),C.$on("gekko_deleted",function(t){return s["a"].commit("deleteGekko",t.id)})},f=function(){l(),d()},h=function(){},m=function(){C.$on("WS_STATUS_CHANGE",function(t){return s["a"].commit("setGlobalWarning",{key:"connected",value:t.connected})})},v=function(){h(),m()},p=a("k5N+"),g=(a("rGqo"),function(t){if(!t)return{};var e=t,a={};return e.forEach(function(t){a[t.slug]=a[t.slug]||{markets:{}},t.markets.forEach(function(e){var n=Object(p["a"])(e["pair"],2),r=n[0],i=n[1];a[t.slug].markets[r]=a[t.slug].markets[r]||[],a[t.slug].markets[r].push(i)}),"exchangeMaxHistoryAge"in t&&(a[t.slug].exchangeMaxHistoryAge=t.exchangeMaxHistoryAge),a[t.slug].importable=!!t.providesFullHistory,a[t.slug].tradable=!!t.tradable,a[t.slug].requires=t.requires}),a}),_=function(){Object(i["a"])("apiKeys",function(t,e){s["a"].commit("syncApiKeys",e)}),Object(i["a"])("exchanges",function(t,e){s["a"].commit("syncExchanges",g(e))})},k=function(){C.$on("apiKeys",function(t){s["a"].commit("syncApiKeys",t.exchanges)})},y=function(){_(),k()},w=function(){u(),f(),v(),y()};a.d(e,"a",function(){return C}),a.d(e,"b",function(){return S});var b=null,C=new n["a"];C.$on("gekko_update",function(t){return console.log(t)}),C.$on("import_update",function(t){return console.log(t)}),C.$on("import_error",function(t){alert("IMPORT ERROR: "+t.error)});var x={connected:!1},S=function(){b=new ReconnectingWebSocket(r["b"],null,{maxReconnectInterval:4e3}),setTimeout(function(){x.connected||(w(),C.$emit("WS_STATUS_CHANGE",x))},500),b.onopen=function(){x.connected||(x.connected=!0,C.$emit("WS_STATUS_CHANGE",x),w())},b.onclose=function(){x.connected&&(x.connected=!1,C.$emit("WS_STATUS_CHANGE",x))},b.onerror=function(){x.connected&&(x.connected=!1,C.$emit("WS_STATUS_CHANGE",x))},b.onmessage=function(t){var e=JSON.parse(t.data);C.$emit(e.type,e)}}},h8ks:function(t,e,a){},hGnM:function(t,e,a){"use strict";var n=function(){var t=this,e=t.$createElement,a=t._self._c||e;return a("div",{staticClass:"contain py2"},[a("h3",[t._v("Market watchers")]),t.watchers.length?t._e():a("div",{staticClass:"text"},[a("p",[t._v("You don't have any market watchers.")])]),t.watchers.length?a("table",{staticClass:"full clickable"},[t._m(0),a("tbody",t._l(t.watchers,function(e){return a("tr",{staticClass:"clickable",on:{click:function(a){t.$router.push({path:"/live-gekkos/"+e.id})}}},[a("td",[t._v(t._s(e.config.watch.exchange))]),a("td",[t._v(t._s(e.config.watch.currency))]),a("td",[t._v(t._s(e.config.watch.asset))]),a("td",[t._v(t._s(t.status(e)))]),a("td",[e.events.initial.candle?[t._v(t._s(t.fmt(e.events.initial.candle.start)))]:t._e()],2),a("td",[e.events.latest.candle?[t._v(t._s(t.fmt(e.events.latest.candle.start)))]:t._e()],2),a("td",[e.events.initial.candle&&e.events.latest.candle?[t._v(t._s(t.timespan(e.events.latest.candle.start,e.events.initial.candle.start)))]:t._e()],2)])}))]):t._e(),a("h3",[t._v("Strat runners")]),t.stratrunners.length?t._e():a("div",{staticClass:"text"},[a("p",[t._v("You don't have any stratrunners.")])]),t.stratrunners.length?a("table",{staticClass:"full"},[t._m(1),a("tbody",t._l(t.stratrunners,function(e){return a("tr",{staticClass:"clickable",on:{click:function(a){t.$router.push({path:"/live-gekkos/"+e.id})}}},[a("td",[t._v(t._s(e.config.watch.exchange))]),a("td",[t._v(t._s(e.config.watch.currency))]),a("td",[t._v(t._s(e.config.watch.asset))]),a("td",[t._v(t._s(t.status(e)))]),a("td",[e.events.initial.candle&&e.events.latest.candle?[t._v(t._s(t.timespan(e.events.latest.candle.start,e.events.initial.candle.start)))]:t._e()],2),a("td",[t._v(t._s(e.config.tradingAdvisor.method))]),a("td",[t.report(e)?t._e():[t._v("0")],t.report(e)?[t._v(t._s(t.round(t.report(e).profit))+" "+t._s(t.report(e).currency))]:t._e()],2),a("td",[t._v(t._s(e.logType))]),a("td",[e.events.tradeCompleted?t._e():[t._v("0")],e.events.tradeCompleted?[t._v(t._s(e.events.tradeCompleted.length))]:t._e()],2)])}))]):t._e(),a("div",{staticClass:"hr"}),a("h2",[t._v("Start a new live Gekko")]),a("router-link",{staticClass:"btn--primary",attrs:{to:"/live-gekkos/new"}},[t._v("Start a new live Gekko!")])],1)},r=[function(){var t=this,e=t.$createElement,a=t._self._c||e;return a("thead",[a("tr",[a("th",[t._v("exchange")]),a("th",[t._v("currency")]),a("th",[t._v("asset")]),a("th",[t._v("status")]),a("th",[t._v("started at")]),a("th",[t._v("last update")]),a("th",[t._v("duration")])])])},function(){var t=this,e=t.$createElement,a=t._self._c||e;return a("thead",[a("tr",[a("th",[t._v("exchange")]),a("th",[t._v("currency")]),a("th",[t._v("asset")]),a("th",[t._v("status")]),a("th",[t._v("duration")]),a("th",[t._v("strategy")]),a("th",[t._v("PnL")]),a("th",[t._v("type")]),a("th",[t._v("trades")])])])}],i=(a("a1Th"),a("rGqo"),a("yt8O"),a("VRzm"),{created:function(){var t=this;this.timer=setInterval(function(){t.now=moment()},1e3)},destroyed:function(){clearTimeout(this.timer)},data:function(){return{timer:!1,now:moment()}},computed:{stratrunners:function(){return _.values(this.$store.state.gekkos).concat(_.values(this.$store.state.archivedGekkos)).filter(function(t){return"papertrader"===t.logType||"tradebot"===t.logType})},watchers:function(){return _.values(this.$store.state.gekkos).concat(_.values(this.$store.state.archivedGekkos)).filter(function(t){return"watcher"===t.logType})}},methods:{humanizeDuration:function(t){return window.humanizeDuration(t)},moment:function(t){function e(e){return t.apply(this,arguments)}return e.toString=function(){return t.toString()},e}(function(t){return moment.utc(t)}),fmt:function(t){return moment.utc(t).format("YYYY-MM-DD HH:mm")},round:function(t){return(+t).toFixed(3)},timespan:function(t,e){return this.humanizeDuration(this.moment(t).diff(this.moment(e)))},status:function(t){return t.errored?"errored":t.stopped?"stopped":t.active?"running":void console.log("unknown state:",t)},report:function(t){return _.get(t,"events.latest.performanceReport")}}}),s=i,o=(a("daul"),a("KHd+")),c=Object(o["a"])(s,n,r,!1,null,null,null);e["a"]=c.exports},jTvs:function(t,e,a){},jVW9:function(t,e,a){},jf14:function(t,e,a){},jloR:function(t,e,a){"use strict";var n=function(){var t=this,e=t.$createElement,a=t._self._c||e;return a("div",{staticClass:"contain my2"},[a("div",{staticClass:"text",domProps:{innerHTML:t._s(t.intro)}}),a("div",{staticClass:"hr"}),a("h3",[t._v("Currently running imports")]),0===t.imports.length?a("p",[t._v("You currently don't have any imports running.")]):t._e(),t.imports.length?a("ul",t._l(t.imports,function(e){return a("li",[a("router-link",{attrs:{to:"/data/importer/import/"+e.id}},[t._v(t._s(e.watch.exchange)+":"+t._s(e.watch.currency)+"/"+t._s(e.watch.asset))])],1)})):t._e(),a("div",{staticClass:"hr"}),a("h3",[t._v("Start a new import")]),a("import-config-builder",{on:{config:t.updateConfig}}),a("div",{staticClass:"hr"}),a("div",{staticClass:"txt--center"},[a("a",{staticClass:"w100--s my1 btn--primary",attrs:{href:"#"},on:{click:function(e){return e.preventDefault(),t.run(e)}}},[t._v("Import")])])],1)},r=[],i=a("wiDz"),s=a("MB/c"),o=function(){var t=this,e=t.$createElement,a=t._self._c||e;return a("div",{staticClass:"grd contain"},[a("div",{staticClass:"grd-row"},[a("div",{staticClass:"grd-row-col-3-6 mx1"},[a("h3",[t._v("Market")]),a("market-picker",{attrs:{"only-importable":"true"},on:{market:t.updateMarketConfig}})],1),a("div",{staticClass:"grd-row-col-3-6 mx1"},[a("range-creator",{on:{range:t.updateRange}})],1)])])},c=[],u=(a("91GP"),a("6BxS")),l=a("FhOJ"),d=(a("LvDl"),{data:function(){return{market:{},range:{}}},components:{marketPicker:u["a"],rangeCreator:l["a"]},computed:{config:function(){var t={};return Object.assign(t,this.market,{importer:{daterange:this.range}},{candleWriter:{enabled:!0}}),t}},methods:{updateMarketConfig:function(t){this.market=t,this.emitConfig()},updateRange:function(t){this.range=t,this.emitConfig()},emitConfig:function(){this.$emit("config",this.config)}}}),f=d,h=(a("H+ir"),a("KHd+")),m=Object(h["a"])(f,o,c,!1,null,null,null),v=m.exports,p=a("5shn"),g=Object(p["a"])("\n\n## Import data\n\nThe importer can download historical market data directly from the exchange.\n\n"),_={components:{importConfigBuilder:v,spinner:s["a"]},data:function(){return{intro:g,config:{}}},computed:{imports:function(){return this.$store.state.imports}},methods:{daysApart:function(t){var e=moment(t.to),a=moment(t.from);return e.diff(a,"days")},updateConfig:function(t){this.config=t},run:function(){var t=this,e=this.daysApart(this.config.importer.daterange);if(e<1)return alert("You can only import at least one day of data..");var a=this.$store.state.exchanges[this.config.watch.exchange];if("exchangeMaxHistoryAge"in a&&moment(this.config.importer.daterange.from) 1%","last 2 versions","not ie <= 8"]}},ldlP:function(t,e,a){"use strict";var n=function(){var t=this,e=t.$createElement,a=t._self._c||e;return a("section",{staticClass:"contain grd-row"},[a("div",{staticClass:"grd-row-col-3-6",domProps:{innerHTML:t._s(t.left)}}),t._m(0)])},r=[function(){var t=this,e=t.$createElement,a=t._self._c||e;return a("div",{staticClass:"grd-row-col-3-6 txt--center"},[a("img",{attrs:{src:"static/gekko.jpg"}}),a("p",[a("em",[t._v("The most valuable commodity I know of is information.")])])])}],i=(a("VRzm"),a("5shn")),s=Object(i["a"])("\n\n## Gekko\n\nGekko is a Bitcoin trading bot and backtesting platform that\nconnects to popular Bitcoin exchanges. It is written in javascript\nand runs on nodejs.\n\n[Find out more](https://gekko.wizb.it/).\n\n*Gekko is 100% free (open source), if you paid for this you have been scammed.*\n\n"),o={data:function(){return{left:s}}},c=o,u=a("KHd+"),l=Object(u["a"])(c,n,r,!1,null,null,null);e["a"]=l.exports},mf2E:function(t,e,a){"use strict";var n=function(){var t=this,e=t.$createElement,a=t._self._c||e;return a("div",{staticClass:"my2"},[t.data?t._e():a("div",{staticClass:"contain"},[a("h1",[t._v("Unknown Gekko instance")]),a("p",[t._v("Gekko doesn't know what gekko this is...")])]),t.data?a("div",[a("h2",{staticClass:"contain"},[t._v("Gekko "+t._s(t.type))]),t.isArchived?a("div",{staticClass:"contain brdr--mid-gray p1 bg--orange"},[t._v("This is an archived Gekko, it is currently not running anymore.")]):t._e(),t.data.errorMessage?a("div",{staticClass:"contain brdr--mid-gray p1 bg--orange"},[t._v("This is Gekko crashed with the following error: "+t._s(t.data.errorMessage))]):t._e(),a("div",{staticClass:"grd contain"},[a("div",{staticClass:"grd-row"},[a("div",{staticClass:"grd-row-col-3-6"},[a("h3",[t._v("Market")]),a("div",{staticClass:"grd-row"},[a("div",{staticClass:"grd-row-col-3-6"},[t._v("Exchange")]),a("div",{staticClass:"grd-row-col-3-6"},[t._v(t._s(t.config.watch.exchange))])]),a("div",{staticClass:"grd-row"},[a("div",{staticClass:"grd-row-col-3-6"},[t._v("Currency")]),a("div",{staticClass:"grd-row-col-3-6"},[t._v(t._s(t.config.watch.currency))])]),a("div",{staticClass:"grd-row"},[a("div",{staticClass:"grd-row-col-3-6"},[t._v("Asset")]),a("div",{staticClass:"grd-row-col-3-6"},[t._v(t._s(t.config.watch.asset))])]),a("div",{staticClass:"grd-row"},[a("div",{staticClass:"grd-row-col-3-6"},[t._v("Type")]),a("div",{staticClass:"grd-row-col-3-6"},[t._v(t._s(t.type))])])]),a("div",{staticClass:"grd-row-col-3-6"},[a("h3",[t._v("Runtime")]),t.isLoading?a("spinner"):t._e(),t.isLoading?t._e():[t.initialEvents.candle?a("div",{staticClass:"grd-row"},[a("div",{staticClass:"grd-row-col-2-6"},[t._v("Watching since")]),a("div",{staticClass:"grd-row-col-4-6"},[t._v(t._s(t.fmt(t.initialEvents.candle.start)))])]):t._e(),t.latestEvents.candle?a("div",{staticClass:"grd-row"},[a("div",{staticClass:"grd-row-col-2-6"},[t._v("Received data until")]),a("div",{staticClass:"grd-row-col-4-6"},[t._v(t._s(t.fmt(t.latestEvents.candle.start)))])]):t._e(),t.latestEvents.candle?a("div",{staticClass:"grd-row"},[a("div",{staticClass:"grd-row-col-2-6"},[t._v("Data spanning")]),a("div",{staticClass:"grd-row-col-4-6"},[t._v(t._s(t.humanizeDuration(t.moment(t.latestEvents.candle.start).diff(t.moment(t.initialEvents.candle.start)))))])]):t._e(),t.isStratrunner?[a("div",{staticClass:"grd-row"},[a("div",{staticClass:"grd-row-col-2-6"},[t._v("Amount of trades")]),a("div",{staticClass:"grd-row-col-4-6"},[t._v(t._s(t.trades.length))])]),a("div",{staticClass:"grd-row"},[a("div",{staticClass:"grd-row-col-2-6"},[t._v("Candle size")]),a("div",{staticClass:"grd-row-col-4-6"},[t._v(t._s(t.config.tradingAdvisor.candleSize))])]),a("div",{staticClass:"grd-row"},[a("div",{staticClass:"grd-row-col-2-6"},[t._v("History size")]),a("div",{staticClass:"grd-row-col-4-6"},[t._v(t._s(t.config.tradingAdvisor.historySize))])])]:t._e()]],2)]),t.warmupRemaining?a("div",{staticClass:"contain brdr--mid-gray p1 bg--orange"},[t._v("This stratrunner is still warming up for the next "),a("i",[t._v(t._s(t.warmupRemaining.replace(","," and ")))]),t._v(", it will not trade until it is warmed up.")]):t._e(),t.isStratrunner?a("div",{staticClass:"grd-row"},[a("div",{staticClass:"grd-row-col-3-6"},[a("h3",[t._v("Strategy")]),a("div",{staticClass:"grd-row"},[a("div",{staticClass:"grd-row-col-3-6"},[t._v("Name")]),a("div",{staticClass:"grd-row-col-3-6"},[a("strong",[t._v(t._s(t.stratName))])])]),t._v("Parameters"),a("pre",[t._v(t._s(t.stratParams))])]),a("div",{staticClass:"grd-row-col-3-6"},[a("h3",[t._v("Profit report")]),t.report?t._e():[a("p",[t.isArchived?a("em",[t._v("This Gekko never executed a trade..")]):t._e(),t.isArchived?t._e():a("em",[t._v("Waiting for at least one trade..")])])],t.report?[a("div",{staticClass:"grd-row"},[a("div",{staticClass:"grd-row-col-3-6"},[t._v("Start balance")]),a("div",{staticClass:"grd-row-col-3-6"},[t._v(t._s(t.round(t.report.startBalance)))])]),a("div",{staticClass:"grd-row"},[a("div",{staticClass:"grd-row-col-3-6"},[t._v("Current balance")]),a("div",{staticClass:"grd-row-col-3-6"},[t._v(t._s(t.round(t.report.balance)))])]),a("div",{staticClass:"grd-row"},[a("div",{staticClass:"grd-row-col-3-6"},[t._v("Market")]),a("div",{staticClass:"grd-row-col-3-6"},[t._v(t._s(t.round(t.report.market/100*t.report.startPrice))+" "+t._s(t.config.watch.currency)+" ("+t._s(t.round(t.report.market))+" %)")])]),a("div",{staticClass:"grd-row"},[a("div",{staticClass:"grd-row-col-3-6"},[t._v("Profit")]),a("div",{staticClass:"grd-row-col-3-6"},[t._v(t._s(t.round(t.report.profit))+" "+t._s(t.config.watch.currency)+" ("+t._s(t.round(t.report.relativeProfit))+" %)")])]),a("div",{staticClass:"grd-row"},[a("div",{staticClass:"grd-row-col-3-6"},[t._v("Alpha")]),a("div",{staticClass:"grd-row-col-3-6"},[t._v(t._s(t.round(t.report.alpha))+" "+t._s(t.config.watch.currency))])])]:t._e()],2)]):t._e(),!t.isStratrunner||t.watcher||t.isArchived?t._e():a("p",[t._v("WARNING: stale gekko, not attached to a watcher, please report "),a("a",{attrs:{href:"https://github.com/askmike/gekko/issues"}},[t._v("here")]),t._v(".")]),t.isArchived?t._e():a("p",[a("a",{staticClass:"w100--s my1 btn--red",on:{click:t.stopGekko}},[t._v("Stop Gekko")])]),t.isArchived?a("p",[a("a",{staticClass:"w100--s my1 btn--red",on:{click:t.deleteGekko}},[t._v("Delete Gekko")])]):t._e(),t.isStratrunner&&t.watcher&&!t.isArchived?a("p",[a("em",[t._v("This gekko gets market data from "),a("router-link",{attrs:{to:"/live-gekkos/"+t.watcher.id}},[t._v("this market watcher")])],1),t._v(".")]):t._e()]),t.isLoading?t._e():[a("h3",{staticClass:"contain"},[t._v("Market graph")]),"fetching"===t.candleFetch?a("spinner"):t._e(),"fetched"===t.candleFetch?[a("chart",{attrs:{data:t.chartData,height:300}})]:t._e(),t.isStratrunner?a("roundtrips",{attrs:{roundtrips:t.roundtrips}}):t._e()]],2):t._e()])},r=[],i=(a("a1Th"),a("dRSK"),a("VRzm"),a("Kw5r")),s=a("LvDl"),o=a.n(s),c=a("wiDz"),u=a("MB/c"),l=a("UeuA"),d=a("vf3E"),f=a("0Bu0"),h={created:function(){this.isLoading||this.getCandles()},components:{spinner:u["a"],chart:l["a"],paperTradeSummary:f["a"],roundtrips:d["a"]},data:function(){return{candleFetch:"idle",candles:!1}},computed:{id:function(){return this.$route.params.id},gekkos:function(){return this.$store.state.gekkos},archivedGekkos:function(){return this.$store.state.archivedGekkos},data:function(){return!!this.gekkos&&(o.a.has(this.gekkos,this.id)?this.gekkos[this.id]:!!o.a.has(this.archivedGekkos,this.id)&&this.archivedGekkos[this.id])},config:function(){return o.a.get(this,"data.config")},latestEvents:function(){return o.a.get(this,"data.events.latest")},initialEvents:function(){return o.a.get(this,"data.events.initial")},trades:function(){return o.a.get(this,"data.events.tradeCompleted")||[]},roundtrips:function(){return o.a.get(this,"data.events.roundtrip")||[]},isLive:function(){return o.a.has(this.gekkos,this.id)},type:function(){return this.data.logType},isStratrunner:function(){return"watcher"!==this.type},isArchived:function(){return this.data.stopped},warmupRemaining:function(){if(!this.isStratrunner)return!1;if(this.isArchived)return!1;if(this.initialEvents.stratWarmupCompleted)return!1;if(!this.initialEvents.candle)return!1;var t=o.a.get(this.config,"tradingAdvisor.historySize");if(!t)return!1;var e=o.a.get(this.config,"tradingAdvisor.candleSize")*t;return humanizeDuration(moment(this.initialEvents.candle.start).add(e,"m").diff(moment()),{largest:2})},chartData:function(){return{candles:this.candles,trades:this.trades}},report:function(){return o.a.get(this.latestEvents,"performanceReport")},stratName:function(){if(this.data)return this.data.config.tradingAdvisor.method},stratParams:function(){if(!this.data)return"Loading...";var t=i["a"].util.extend({},this.data.config[this.stratName]);return delete t.__empty,o.a.isEmpty(t)?"No parameters":JSON.stringify(t,null,4)},isLoading:function(){return!this.data||(!o.a.get(this.data,"events.initial.candle")||!o.a.get(this.data,"events.latest.candle"))},watcher:function(){var t=this;if(!this.isStratrunner)return!1;var e=i["a"].util.extend({},this.data.config.watch);return o.a.find(this.gekkos,function(a){return a.id!==t.id&&o.a.isEqual(e,a.config.watch)})},hasLeechers:function(){var t=this;if(this.isStratrunner)return!1;var e=i["a"].util.extend({},this.data.config.watch);return o.a.find(this.gekkos,function(a){return a.id!==t.id&&o.a.isEqual(e,a.config.watch)})}},watch:{"data.events.latest.candle.start":function(){setTimeout(this.getCandles,o.a.random(100,2e3))}},methods:{round:function(t){return(+t).toFixed(5)},humanizeDuration:function(t,e){return window.humanizeDuration(t,e)},moment:function(t){function e(e){return t.apply(this,arguments)}return e.toString=function(){return t.toString()},e}(function(t){return moment.utc(t)}),fmt:function(t){return moment.utc(t).format("YYYY-MM-DD HH:mm")},getCandles:function(){var t=this;if(!this.isLoading&&"fetching"!==this.candleFetch){this.candleFetch="fetching";var e=this.data.events.latest.candle.start,a=this.data.events.initial.candle.start,n=1;"watcher"!==this.type&&(n=this.data.config.tradingAdvisor.candleSize);var r={watch:this.data.config.watch,daterange:{to:e,from:a},candleSize:n};setTimeout(function(){Object(c["b"])("getCandles",r,function(e,a){if(t.candleFetch="fetched",!a||a.error||!o.a.isArray(a))return console.log(a);t.candles=a.map(function(t){return t.start=moment.unix(t.start).utc().format(),t})})},o.a.random(150,2500))}},stopGekko:function(){if(this.hasLeechers)return alert("This Gekko is fetching market data for multiple stratrunners, stop these first.");confirm("Are you sure you want to stop this Gekko?")&&Object(c["b"])("stopGekko",{id:this.data.id},function(t,e){console.log("stopped gekko")})},deleteGekko:function(){var t=this;if(!this.isArchived)return alert("This Gekko is still running, stop it first!");confirm("Are you sure you want to delete this Gekko?")&&Object(c["b"])("deleteGekko",{id:this.data.id},function(e,a){t.$router.push({path:"/live-gekkos/"})})}}},m=h,v=(a("/Dpa"),a("KHd+")),p=Object(v["a"])(m,n,r,!1,null,null,null);e["a"]=p.exports},ngcj:function(t,e,a){"use strict";var n=a("EAJ1"),r=a.n(n);r.a},nzng:function(t,e,a){},pd3X:function(t,e,a){},pyMo:function(t,e,a){},q431:function(t,e,a){"use strict";var n=a("ydmZ"),r=a.n(n);r.a},qDq4:function(t,e,a){"use strict";var n=a("nzng"),r=a.n(n);r.a},r0f2:function(t,e,a){"use strict";a.d(e,"b",function(){return i}),a.d(e,"a",function(){return r});var n,r,i,s=window.CONFIG.ui,o="".concat(s.host).concat(80===s.port?"":":".concat(s.port)).concat(s.path);n=s.ssl?"https://".concat(o):"http://".concat(o),r=n+"api/",i=s.ssl?"wss://".concat(o,"api"):"ws://".concat(o,"api")},rloZ:function(t,e,a){"use strict";var n=function(){var t=this,e=t.$createElement,a=t._self._c||e;return a("div",{staticClass:"grd"},[a("div",{staticClass:"px1"},[a("h3",[t._v("Paper trader")]),"closed"===t.toggle?a("a",{staticClass:"btn--primary",attrs:{href:"#"},on:{click:function(e){return e.preventDefault(),t.switchToggle(e)}}},[t._v("Change paper trader settings")]):t._e(),"open"===t.toggle?[a("p",[t._v("Settings:")]),a("textarea",{directives:[{name:"model",rawName:"v-model",value:t.rawPaperTraderParams,expression:"rawPaperTraderParams"}],staticClass:"params",domProps:{value:t.rawPaperTraderParams},on:{input:function(e){e.target.composing||(t.rawPaperTraderParams=e.target.value)}}}),t.rawPaperTraderParamsError?a("p",{staticClass:"bg--red p1"},[t._v(t._s(t.rawPaperTraderParamsError.message))]):t._e()]:t._e()],2)])},r=[],i=(a("LvDl"),a("wiDz")),s={created:function(){var t=this;Object(i["a"])("configPart/paperTrader",function(e,a){t.rawPaperTraderParams=a.part})},data:function(){return{rawPaperTraderParams:"",rawPaperTraderParamsError:!1,paperTraderParams:{},toggle:"closed"}},watch:{rawPaperTraderParams:function(){this.emitConfig()}},methods:{switchToggle:function(){"open"===this.toggle?this.toggle="closed":this.toggle="open"},emitConfig:function(){this.parseParams(),this.$emit("settings",this.paperTraderParams)},parseParams:function(){try{this.paperTraderParams=toml.parse(this.rawPaperTraderParams),this.paperTraderParams.reportRoundtrips=!0,this.rawPaperTraderParamsError=!1}catch(t){this.rawPaperTraderParamsError=t,this.paperTraderParams={}}}}},o=s,c=(a("0zrD"),a("KHd+")),u=Object(c["a"])(o,n,r,!1,null,null,null);e["a"]=u.exports},spvI:function(t,e,a){"use strict";a("Vd3H"),a("rGqo");var n=a("wiDz"),r={data:function(){return{datasets:[],datasetScanstate:"idle",unscannableMakets:[]}},methods:{scan:function(){var t=this;this.datasetScanstate="scanning",Object(n["b"])("scansets",{},function(e,a){t.datasetScanstate="scanned",t.unscannableMakets=a.errors;var n=[];a.datasets.forEach(function(t){t.ranges.forEach(function(e,a){n.push({exchange:t.exchange,currency:t.currency,asset:t.asset,from:moment.unix(e.from).utc(),to:moment.unix(e.to).utc(),id:t.exchange+t.asset+t.currency+a})})}),n=n.filter(function(t){if(t.to.diff(t.from,"hours")>2)return!0}),n=n.sort(function(t,e){var a=t.to.diff(t.from),n=e.to.diff(e.from);return an?1:0}).reverse(),t.datasets=n})}}};e["a"]=r},tr8f:function(t,e,a){"use strict";var n=a("h8ks"),r=a.n(n);r.a},tr8z:function(t,e,a){"use strict";var n=a("Ug4+"),r=a.n(n);r.a},uMTv:function(t,e,a){"use strict";var n=a("pyMo"),r=a.n(n);r.a},uXxc:function(t,e,a){},vf3E:function(t,e,a){"use strict";var n=function(){var t=this,e=t.$createElement,a=t._self._c||e;return a("div",{staticClass:"contain roundtrips"},[a("h2",[t._v("Roundtrips")]),t.roundtrips.length?a("table",[a("thead",[t._m(0),t._l(t.roundtrips,function(e){return a("tr",[a("td",[t._v(t._s(t.fmt(e.entryAt)))]),a("td",[t._v(t._s(t.fmt(e.exitAt)))]),a("td",[t._v(t._s(t.diff(e.duration)))]),a("td",[t._v(t._s(t.round(e.entryBalance)))]),a("td",[t._v(t._s(t.round(e.exitBalance)))]),-1===Math.sign(e.pnl)?[a("td",{staticClass:"loss"},[t._v(t._s(Math.sign(e.pnl)*e.pnl.toFixed(2)))]),a("td",{staticClass:"loss"},[t._v(t._s(e.profit.toFixed(2))+"%")])]:[a("td",{staticClass:"profit"},[t._v(t._s(e.pnl.toFixed(2)))]),a("td",{staticClass:"profit"},[t._v(t._s(e.profit.toFixed(2))+"%")])]],2)})],2)]):t._e(),t.roundtrips.length?t._e():a("div",[a("p",[t._v("Not enough data to display")])])])},r=[function(){var t=this,e=t.$createElement,a=t._self._c||e;return a("tr",[a("th",[t._v("Entry at (UTC)")]),a("th",[t._v("Exit at (UTC)")]),a("th",[t._v("Exposure")]),a("th",[t._v("Entry balance")]),a("th",[t._v("Exit balance")]),a("th",[t._v("P&L")]),a("th",[t._v("Profit")])])}],i=a("LvDl"),s=a.n(i),o={props:["roundtrips"],data:function(){return{}},methods:{diff:function(t){return moment.duration(t).humanize()},humanizeDuration:function(t){return window.humanizeDuration(t)},fmt:function(t){var e;return e=s.a.isNumber(t)?moment.unix(t):moment(t).utc(),e.utc().format("YYYY-MM-DD HH:mm")},round:function(t){return(+t).toFixed(3)}}},c=o,u=(a("dW8q"),a("KHd+")),l=Object(u["a"])(c,n,r,!1,null,null,null);e["a"]=l.exports},vwRV:function(t,e,a){},w3yp:function(t,e,a){},wVP4:function(t,e,a){"use strict";var n=a("vwRV"),r=a.n(n);r.a},wVPO:function(t,e,a){"use strict";var n=a("uXxc"),r=a.n(n);r.a},wiDz:function(t,e,a){"use strict";a.d(e,"b",function(){return u}),a.d(e,"a",function(){return l});var n=a("24Ii"),r=a.n(n),i=a("TrxG"),s=a.n(i),o=a("r0f2"),c=function(t){return function(e,a){if(e)return t(e);if(!a.text)return t("no data");var n=JSON.parse(a.text);t(!1,n)}},u=function(t,e,a){r.a.post(o["a"]+t).use(s.a).send(e).end(c(a))},l=function(t,e){r.a.get(o["a"]+t).use(s.a).end(c(e))}},ydmZ:function(t,e,a){},yomC:function(t,e,a){"use strict";var n=a("EfWa"),r=a.n(n);r.a},yuKf:function(t,e,a){}}); //# sourceMappingURL=app.9aa8dda3.js.map \ No newline at end of file From 0348b0febf8671009e229d3c278e9c3c1ad43699 Mon Sep 17 00:00:00 2001 From: Mike van Rossum Date: Sat, 11 Aug 2018 20:52:31 +0800 Subject: [PATCH 14/29] [DOCS] improve readability of strat docs --- docs/strategies/creating_a_strategy.md | 108 +++++++------------------ 1 file changed, 30 insertions(+), 78 deletions(-) diff --git a/docs/strategies/creating_a_strategy.md b/docs/strategies/creating_a_strategy.md index 8b8851677..285107d18 100644 --- a/docs/strategies/creating_a_strategy.md +++ b/docs/strategies/creating_a_strategy.md @@ -1,14 +1,14 @@ # Creating a strategy -Strategies are the core of Gekko's trading bot. They look at the market and decide what to do based on technical analysis indicators. As of now all strategies are limited to a single market on a single exchange. +Strategies are the core of Gekko's trading bot. They look at the market and decide what to do based on technical analysis indicators. A single strategy is limited to a single market on a single exchange. Gekko currently comes with [a couple of strategies](./introduction.md) out of the box. Besides those you can also write your own strategy in javascript. The easiest way to do this is to customize the file `gekko/strategies/custom.js`. ## Creating a strategy -A strategy is a combination of functions that get market data in the form of candles ([OHLC](https://en.wikipedia.org/wiki/Open-high-low-close_chart), volume, and the average weighted price). +A strategy is a module with a few functions that get market data in the form of candles ([OHLC](https://en.wikipedia.org/wiki/Open-high-low-close_chart), volume, and the average weighted price) and output trading advice. -## Boilerplate strategies +## Strategy boilerplate // Let's create our own strategy var strat = {}; @@ -46,27 +46,32 @@ A strategy is a combination of functions that get market data in the form of can module.exports = strat; -The above boilerplate contains four functions that must be completed. The functions are executed like so: +# Strategy lifecycle methods -- On startup: run init. -- On each new candle: run update. - - if required history has been built (See `check()` function below): run log, run check. +The above boilerplate contains four functions. These functions are executed by Gekko like so: + +- When Gekko starts: run init. +- On each new candle: + - run [update](#update-function) + - once the strategy has completed warmup: + - run [log](#log-function) + - run [check](#check-function) ### init function -Executed when the trading strategy starts. Initialize trading parameters here. +Executed when the trading strategy starts. Your strategy can initialize state and [register indicators](#Indicators). ### update function -This function executes on every new candle. Refresh trading parameters here. +This function executes on every new candle. You can access the latest candle as the first (and only) parameter (it's also stored in `this.candle`). ### log function -The log function is executed when the `debug` flag configuration is on (Set this in the config). Logging is used to trace parameter values as the `init` and `update` functions are executed over time. +The log function is executed on every new candle when the `debug` flag is on (always off when running in the UI, as configured in [the config](../commandline/about_the_commandline.md) for CLI gekkos). Logging is used to log certain state from the strategy and can be used to debug your strategy to get more insights in why it took certain decisions. ### check function -Most strategies need a minimal amount of history before the trading strategy can be started. For example the strategy may be calculating a moving average for the first 3 candles, so it must have at least 3 candles to start. The check function is executed after the required history period is over. The default required history is 0. You can set it like so in your init function: +Most strategies need to warmup before the trading strategy can be started. For example the strategy may be calculating a moving average for the first 3 candles, as such it must have at least 3 candles to output a number the strategy logic relies on. The check function is executed after the warmup period is over. The default required history is 0. You can set it like so in your init function: this.requiredHistory = 5; // require 5 candles before giving advice @@ -76,9 +81,9 @@ If you find out in the check function that you want to give new advice to the tr // or this.advice('long'); -### candle variables +### candle variable -The following list of candle variables can be used when writing strategies: +The following list of candle variables will be available when writing strategies: - candle.close: the closing price of the candle - candle.high: the highest price of the candle @@ -86,60 +91,7 @@ The following list of candle variables can be used when writing strategies: - candle.volume: the trading volume of that candle - candle.trades: number of trades in that candle -Keep in mind that these variables will give you different results depending on the time window set (1 minute, 15 minutes, 1 hour, etc.) for constructing the candle. - - -### basic strategy example - -This a basic strategy example that buys and sells BTC/USDT when it hits a specific price. - - // Let's create our own buy and sell strategy - var strat = {}; - - // Prepare everything our strat needs - strat.init = function() { - // setting buy price - this.buyPrice = 2000; - - // setting sell price - this.sellPrice = 2500; - } - - // What happens on every new candle? - strat.update = function(candle) { - // your code! - } - - // For debugging purposes. - strat.log = function() { - // your code! - } - - // Based on the newly calculated - // information, check if we should - // update or not. - strat.check = function(candle) { - // buy when it hits buy price - if(candle.close <= this.buyPrice) { - this.advice("long"); - // do some output - console.log("buying BTC @", candle.close); - return; - } - - // sell when it hits sell price - if(candle.close >= this.sellPrice) { - this.advice("short"); - // do some output - console.log("selling BTC @", candle.close); - console.log("Profit:", (candle.close-this.buyPrice)); - return; - } - } - - module.exports = strat; - -## Strategy rules +## Things to keep in mind - You can activate your own strategy by setting `config.tradingAdvisor.strategy` to `custom` (or whatever you named your file inside the `gekko/strategies`) in the loaded config. - Gekko will execute the `update` function for every new candle. A candle is the size in minutes configured at `config.tradingAdvisor.candleSize` in the loaded config. @@ -173,7 +125,7 @@ or The first parameter is the name, the second is the indicator type you want and the third is an object with all indicator parameters. If you want an MACD indicator you can do it like so: -In your init method: +In your init function: // add a native indicator var parameters = {short: 10, long: 20, signal: 9}; @@ -187,7 +139,7 @@ In your init method: var parameters = {optInFastPeriod: 10, optInSlowPeriod: 21, optInSignalPeriod: 9}; this.addTulipIndicator('mytulipmacd', 'macd', parameters); -In your check or update method: +In your check or update function: var result = this.indicators.mytalibmacd.result; @@ -195,43 +147,43 @@ See the [TA-lib indicators](./talib_indicators.md) document for a list of all su See the [Tulip indicators](./tulip_indicators.md) document for a list of all supported Tulip indicators and their required parameters. -### Configurables +### Strategy parameters -Adjust method execution by creating custom configuration parameters. This way the same method can execute strategies concurrently using different parameters for different markets. Create custom configuration settings in the `config/strategies` directory: +Adjust strategy execution by creating custom strategy parameters. This way the same strategy can execute strategies concurrently using different parameters for different markets. For example the MACD strategy has parameters concerning the underlying MACD indicator (such as values for the LONG and SHORT EMAs). Create custom configuration settings in the `config/strategies` directory: // custom settings: config.custom = { my_custom_setting: 10, }; -Retrieve them in your method like this: +Retrieve them in your strategy like this: // anywhere in your code: log.debug(this.settings.my_custom_setting); // Logs 10 ___Note that the name of your configuration must be the same as the name of the strategy___ -### Tool libraries +### External libraries Gekko uses a few general purpose libraries internally. The API from those libraries are available to you as well. Most notable libraries are [lodash](http://lodash.com/) (similar as underscore) and [async](https://github.com/caolan/async). You can load them like so: - // before any methods + // before any other code var _ = require('lodash'); var async = require('async'); ### Logging -Gekko has a small logger you can use (preferably in your log method): +Gekko has a small logger you can use (preferably in your log function): - // before any methods + // before any other code var log = require('../core/log.js'); - // in your log method + // in your log function log.debug('hello world'); ----- -Take a look at the existing methods, if you have questions feel free to create an issue. If you created your own awesome trading method and want to share it with the world feel free to contribute it to gekko. +Take a look at the existing methods, if you have questions feel free to create an issue. If you created your own awesome strategies and want to share it with the world feel free to contribute it to gekko. From 3a166de25473eb462389044493679a8d4a3352e4 Mon Sep 17 00:00:00 2001 From: Mike van Rossum Date: Sat, 11 Aug 2018 21:19:50 +0800 Subject: [PATCH 15/29] [GB] update poloniex dep --- exchange/package-lock.json | 198 +++++++++---------------------------- exchange/package.json | 2 +- 2 files changed, 47 insertions(+), 153 deletions(-) diff --git a/exchange/package-lock.json b/exchange/package-lock.json index faaaf981d..e7c2b1ea2 100644 --- a/exchange/package-lock.json +++ b/exchange/package-lock.json @@ -154,14 +154,6 @@ "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.1.tgz", "integrity": "sha512-MKiLiV+I1AA596t9w1sQJ8jkiSr5+ZKi0WKrYGUn6d1Fx+Ij4tIj+m2WMQSGczs5jZVxV339chE8iwk6F64wjA==" }, - "boom": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/boom/-/boom-0.4.2.tgz", - "integrity": "sha1-emNune1O/O+xnO9JR6PGffrukRs=", - "requires": { - "hoek": "0.9.1" - } - }, "caseless": { "version": "0.12.0", "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", @@ -220,21 +212,6 @@ "printj": "1.1.2" } }, - "cryptiles": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-0.2.2.tgz", - "integrity": "sha1-7ZH/HxetE9N0gohZT4pIoNJvMlw=", - "optional": true, - "requires": { - "boom": "0.4.2" - } - }, - "ctype": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/ctype/-/ctype-0.5.3.tgz", - "integrity": "sha1-gsGMJGH3QRTvFsE1IkrQuRRMoS8=", - "optional": true - }, "dashdash": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", @@ -420,23 +397,6 @@ "has-symbol-support-x": "1.4.2" } }, - "hawk": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/hawk/-/hawk-1.0.0.tgz", - "integrity": "sha1-uQuxaYByhUEdp//LjdJZhQLTtS0=", - "optional": true, - "requires": { - "boom": "0.4.2", - "cryptiles": "0.2.2", - "hoek": "0.9.1", - "sntp": "0.2.4" - } - }, - "hoek": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/hoek/-/hoek-0.9.1.tgz", - "integrity": "sha1-PTIkYrrfB3Fup+uFuviAec3c5QU=" - }, "http-signature": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", @@ -547,11 +507,6 @@ "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==" }, - "mime": { - "version": "1.2.11", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.2.11.tgz", - "integrity": "sha1-WCA+7Ybjpe8XrtK32evUfwpg3RA=" - }, "mime-db": { "version": "1.35.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.35.0.tgz", @@ -624,118 +579,61 @@ "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" }, "poloniex.js": { - "version": "git://github.com/askmike/poloniex.js.git#69f5e254353e66d135070844fc3328efcbe3641c", + "version": "git://github.com/askmike/poloniex.js.git#0d0483e48c550cb5c547398d210785e5fc9adb71", "requires": { "nonce": "1.0.4", - "request": "2.33.0" + "request": "2.88.0" }, "dependencies": { - "asn1": { - "version": "0.1.11", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.1.11.tgz", - "integrity": "sha1-VZvhg3bQik7E2+gId9J4GGObLfc=", - "optional": true - }, - "assert-plus": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.1.5.tgz", - "integrity": "sha1-7nQAlBMALYTOxyGcasgRgS5yMWA=", - "optional": true - }, - "async": { - "version": "0.9.2", - "resolved": "https://registry.npmjs.org/async/-/async-0.9.2.tgz", - "integrity": "sha1-rqdNXmHB+JlhO/ZL2mbUx48v0X0=", - "optional": true - }, - "aws-sign2": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.5.0.tgz", - "integrity": "sha1-xXED96F/wDfwLXwuZLYC6iI/fWM=", - "optional": true - }, - "combined-stream": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-0.0.7.tgz", - "integrity": "sha1-ATfmV7qlp1QcV6w3rF/AfXO03B8=", - "optional": true, - "requires": { - "delayed-stream": "0.0.5" - } - }, - "delayed-stream": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-0.0.5.tgz", - "integrity": "sha1-1LH0OpPoKW3+AmlPRoC8N6MTxz8=", - "optional": true - }, - "forever-agent": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.5.2.tgz", - "integrity": "sha1-bQ4JxJIflKJ/Y9O0nF/v8epMUTA=" - }, - "form-data": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-0.1.4.tgz", - "integrity": "sha1-kavXiKupcCsaq/qLwBAxoqyeOxI=", - "optional": true, + "har-validator": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.0.tgz", + "integrity": "sha512-+qnmNjI4OfH2ipQ9VQOw23bBd/ibtfbVdK2fYbY4acTDqKTW/YDp9McimZdDbG8iV9fZizUqQMD5xvriB146TA==", "requires": { - "async": "0.9.2", - "combined-stream": "0.0.7", - "mime": "1.2.11" + "ajv": "5.5.2", + "har-schema": "2.0.0" } }, - "http-signature": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-0.10.1.tgz", - "integrity": "sha1-T72sEyVZqoMjEh5UB3nAoBKyfmY=", - "optional": true, - "requires": { - "asn1": "0.1.11", - "assert-plus": "0.1.5", - "ctype": "0.5.3" - } - }, - "node-uuid": { - "version": "1.4.8", - "resolved": "https://registry.npmjs.org/node-uuid/-/node-uuid-1.4.8.tgz", - "integrity": "sha1-sEDrCSOWivq/jTL7HxfxFn/auQc=" - }, "oauth-sign": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.3.0.tgz", - "integrity": "sha1-y1QPk7srIqfVlBaRoojWDo6pOG4=", - "optional": true - }, - "qs": { - "version": "0.6.6", - "resolved": "https://registry.npmjs.org/qs/-/qs-0.6.6.tgz", - "integrity": "sha1-bgFQmP9RlouKPIGQAdXyyJvEsQc=" + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==" }, "request": { - "version": "2.33.0", - "resolved": "https://registry.npmjs.org/request/-/request-2.33.0.tgz", - "integrity": "sha1-UWeHgTFyYHDsYzdS6iMKI3ncZf8=", + "version": "2.88.0", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz", + "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==", "requires": { - "aws-sign2": "0.5.0", - "forever-agent": "0.5.2", - "form-data": "0.1.4", - "hawk": "1.0.0", - "http-signature": "0.10.1", + "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": "1.2.11", - "node-uuid": "1.4.8", - "oauth-sign": "0.3.0", - "qs": "0.6.6", - "tough-cookie": "2.3.4", - "tunnel-agent": "0.3.0" + "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" } }, - "tunnel-agent": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.3.0.tgz", - "integrity": "sha1-rWgbaPUyGtKCfEz7G31d8s/pQu4=", - "optional": true + "tough-cookie": { + "version": "2.4.3", + "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.29", + "punycode": "1.4.1" + } } } }, @@ -749,6 +647,11 @@ "resolved": "https://registry.npmjs.org/printj/-/printj-1.1.2.tgz", "integrity": "sha512-zA2SmoLaxZyArQTOPj5LXecR+RagfPSU5Kw1qP+jkWeNlrq+eJZyY2oS68SU1Z/7/myXM4lo9716laOFAVStCQ==" }, + "psl": { + "version": "1.1.29", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.1.29.tgz", + "integrity": "sha512-AeUmQ0oLN02flVHXWh9sSJF7mcdFq0ppid/JkErufc3hGIV/AMa8Fo9VgDo/cT2jFdOWoFvHp90qqBH54W+gjQ==" + }, "punycode": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", @@ -833,15 +736,6 @@ "websocket": "1.0.26" } }, - "sntp": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/sntp/-/sntp-0.2.4.tgz", - "integrity": "sha1-+4hfGLDzqtGJ+CSGJTa87ux1CQA=", - "optional": true, - "requires": { - "hoek": "0.9.1" - } - }, "sshpk": { "version": "1.14.2", "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.14.2.tgz", diff --git a/exchange/package.json b/exchange/package.json index ebf2d6394..9d359cf95 100644 --- a/exchange/package.json +++ b/exchange/package.json @@ -30,7 +30,7 @@ "kraken-api": "^1.0.0", "lodash": "^4.17.5", "moment": "^2.22.1", - "poloniex.js": "git://github.com/askmike/poloniex.js.git#69f5e254353e66d135070844fc3328efcbe3641c", + "poloniex.js": "git://github.com/askmike/poloniex.js.git#0d0483e48c550cb5c547398d210785e5fc9adb71", "request-promise": "^4.2.2", "retry": "^0.12.0", "gekko-bittrex": "^0.8.5" From 9c9bf8b93e15cdb199e8f8ebeadfae20fa98072a Mon Sep 17 00:00:00 2001 From: Mike van Rossum Date: Sun, 12 Aug 2018 09:53:11 +0800 Subject: [PATCH 16/29] use rewritten polo dep (#2433) * use rewritten polo dep * update polo dep to v0.0.11 --- exchange/package-lock.json | 72 +++++--------------------------------- exchange/package.json | 2 +- 2 files changed, 9 insertions(+), 65 deletions(-) diff --git a/exchange/package-lock.json b/exchange/package-lock.json index e7c2b1ea2..eb6b82b7a 100644 --- a/exchange/package-lock.json +++ b/exchange/package-lock.json @@ -336,6 +336,14 @@ "signalr-client": "0.0.17" } }, + "gekko-broker-poloniex": { + "version": "0.0.11", + "resolved": "https://registry.npmjs.org/gekko-broker-poloniex/-/gekko-broker-poloniex-0.0.11.tgz", + "integrity": "sha512-N2Sv6ZOvsrLWT5GJ0tuowsOz2X0UmsQMYrMEOJQtuHqbxIfM87t2eaat/2DXed1z4XjldVe5GXIsLbOL9bKVwQ==", + "requires": { + "nonce": "1.0.4" + } + }, "get-stream": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", @@ -578,65 +586,6 @@ "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" }, - "poloniex.js": { - "version": "git://github.com/askmike/poloniex.js.git#0d0483e48c550cb5c547398d210785e5fc9adb71", - "requires": { - "nonce": "1.0.4", - "request": "2.88.0" - }, - "dependencies": { - "har-validator": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.0.tgz", - "integrity": "sha512-+qnmNjI4OfH2ipQ9VQOw23bBd/ibtfbVdK2fYbY4acTDqKTW/YDp9McimZdDbG8iV9fZizUqQMD5xvriB146TA==", - "requires": { - "ajv": "5.5.2", - "har-schema": "2.0.0" - } - }, - "oauth-sign": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", - "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==" - }, - "request": { - "version": "2.88.0", - "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" - } - }, - "tough-cookie": { - "version": "2.4.3", - "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.29", - "punycode": "1.4.1" - } - } - } - }, "prepend-http": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz", @@ -647,11 +596,6 @@ "resolved": "https://registry.npmjs.org/printj/-/printj-1.1.2.tgz", "integrity": "sha512-zA2SmoLaxZyArQTOPj5LXecR+RagfPSU5Kw1qP+jkWeNlrq+eJZyY2oS68SU1Z/7/myXM4lo9716laOFAVStCQ==" }, - "psl": { - "version": "1.1.29", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.1.29.tgz", - "integrity": "sha512-AeUmQ0oLN02flVHXWh9sSJF7mcdFq0ppid/JkErufc3hGIV/AMa8Fo9VgDo/cT2jFdOWoFvHp90qqBH54W+gjQ==" - }, "punycode": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", diff --git a/exchange/package.json b/exchange/package.json index 9d359cf95..766605e9f 100644 --- a/exchange/package.json +++ b/exchange/package.json @@ -30,7 +30,7 @@ "kraken-api": "^1.0.0", "lodash": "^4.17.5", "moment": "^2.22.1", - "poloniex.js": "git://github.com/askmike/poloniex.js.git#0d0483e48c550cb5c547398d210785e5fc9adb71", + "gekko-broker-poloniex": "^0.0.11", "request-promise": "^4.2.2", "retry": "^0.12.0", "gekko-bittrex": "^0.8.5" From cdb3d9333b38ff9eb444824cc059eb56257247ae Mon Sep 17 00:00:00 2001 From: Mike van Rossum Date: Sun, 12 Aug 2018 19:21:34 +0800 Subject: [PATCH 17/29] use v0.0.12 of polo dep (#2435) * use rewritten polo dep * update polo dep to v0.0.11 * use v0.0.12 of polo dep --- exchange/package-lock.json | 6 +++--- exchange/package.json | 2 +- exchange/wrappers/poloniex.js | 8 ++++++-- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/exchange/package-lock.json b/exchange/package-lock.json index eb6b82b7a..e6e5b135a 100644 --- a/exchange/package-lock.json +++ b/exchange/package-lock.json @@ -337,9 +337,9 @@ } }, "gekko-broker-poloniex": { - "version": "0.0.11", - "resolved": "https://registry.npmjs.org/gekko-broker-poloniex/-/gekko-broker-poloniex-0.0.11.tgz", - "integrity": "sha512-N2Sv6ZOvsrLWT5GJ0tuowsOz2X0UmsQMYrMEOJQtuHqbxIfM87t2eaat/2DXed1z4XjldVe5GXIsLbOL9bKVwQ==", + "version": "0.0.12", + "resolved": "https://registry.npmjs.org/gekko-broker-poloniex/-/gekko-broker-poloniex-0.0.12.tgz", + "integrity": "sha512-fyR+zljTvRmKJoPoBl8oQl91sLmZy6xoitIUjjF+xV1vUKzwYJ3uHaG8VPQ3JP4sHcKZ6pZfbkftCepPEOph3g==", "requires": { "nonce": "1.0.4" } diff --git a/exchange/package.json b/exchange/package.json index 766605e9f..8520f6e8b 100644 --- a/exchange/package.json +++ b/exchange/package.json @@ -30,7 +30,7 @@ "kraken-api": "^1.0.0", "lodash": "^4.17.5", "moment": "^2.22.1", - "gekko-broker-poloniex": "^0.0.11", + "gekko-broker-poloniex": "^0.0.12", "request-promise": "^4.2.2", "retry": "^0.12.0", "gekko-bittrex": "^0.8.5" diff --git a/exchange/wrappers/poloniex.js b/exchange/wrappers/poloniex.js index 5f3d03b05..96c9b03d2 100644 --- a/exchange/wrappers/poloniex.js +++ b/exchange/wrappers/poloniex.js @@ -1,4 +1,4 @@ -const Poloniex = require("poloniex.js"); +const Poloniex = require("gekko-broker-poloniex"); const _ = require('lodash'); const moment = require('moment'); const retry = require('../exchangeUtils').retry; @@ -18,7 +18,11 @@ const Trader = function(config) { this.pair = this.currency + '_' + this.asset; - this.poloniex = new Poloniex(this.key, this.secret); + this.poloniex = new Poloniex({ + key: this.key, + secret: this.secret, + userAgent: 'Gekko Broker v' + require('../package.json').version + }); } const recoverableErrors = [ From dccab504cc0933219b2b974a2eedd63cb30b4aa3 Mon Sep 17 00:00:00 2001 From: Mike van Rossum Date: Sun, 12 Aug 2018 19:32:39 +0800 Subject: [PATCH 18/29] [GB] rm polo verbose logging --- exchange/wrappers/poloniex.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/exchange/wrappers/poloniex.js b/exchange/wrappers/poloniex.js index 96c9b03d2..2f1d70fae 100644 --- a/exchange/wrappers/poloniex.js +++ b/exchange/wrappers/poloniex.js @@ -154,8 +154,6 @@ Trader.prototype.processResponse = function(next, fn, payload) { } if(fn === 'order') { - console.log('order', error.message); - if(includes(error.message, ['Not enough'])) { error.retry = 2; } @@ -374,8 +372,6 @@ Trader.prototype.cancelOrder = function(order, callback) { return callback(err); } - console.log(new Date, 'cancel', result.amount); - if(result.filled) { return callback(undefined, true); } From 6f49c722ef7a4c8da64c1439c7be666b4f1d73e3 Mon Sep 17 00:00:00 2001 From: Mike van Rossum Date: Sun, 12 Aug 2018 22:18:25 +0800 Subject: [PATCH 19/29] [DOCS] add docs on papertrader, tradebot rewrite simulation explanation in backtester --- docs/features/backtesting.md | 12 +++++++++--- docs/features/paper_trading.md | 7 +++++++ docs/features/trading_bot.md | 22 ++++++++++++++++++++++ 3 files changed, 38 insertions(+), 3 deletions(-) create mode 100644 docs/features/paper_trading.md create mode 100644 docs/features/trading_bot.md diff --git a/docs/features/backtesting.md b/docs/features/backtesting.md index caac34ac2..2c3d8dc28 100644 --- a/docs/features/backtesting.md +++ b/docs/features/backtesting.md @@ -1,14 +1,20 @@ # Backtesting -Gekko supports backtesting strategies over historical data. This means that Gekko will simulate running a strategy in realtime against a live market. Backtesting requires having data locally available already. After a backtest Gekko will provide statistics about the market and the strategy's performance. +Gekko supports backtesting strategies over historical data. A Backtest is a simulation where you simulate running a strategy over a long time (such as the last 30 days) in a matter of seconds. Backtesting requires having market data locally available already. After a backtest Gekko will provide statistics about the market and the strategy's performance. ![screen shot of gekko backtesting](https://cloud.githubusercontent.com/assets/969743/24838718/8c790a86-1d45-11e7-99ae-e7e551cb40cb.png) +**Important things to remember:** + +- Just because a strategy performed well in the past, does not mean it will perform well in the future. +- Be careful of overfitting, in other words: don't simply tweak a strategy until you get high profit and assume that will be as profitable when going live. Read more about overfitting in [this article](https://laplaceinsights.com/backtesting-strategies-and-overfitting/). +- The backtest simulation is limited, this is not really a problem on bigger markets (such as BTC/USD) but the differences between backtests and live traders on very low volume markets might be big. Read more about this below in the simplified simulation below. + ## Simplified simulation -Gekko backtests using a very limited datasource (only OHCL candles). This means that Gekko estimates trades (and thus profits), which depending on the liquidity and market depth might be estimated very wrong. By configuring the paper trader's fee and slippage you can better mimic trading at the real market. +Simulating trades is done through a module called the paper trader. This module will use market candles together with fee, slippage and spread numbers to estimate trade executions costs. While the default settings work great for most big markets (USD/BTC or BTC/ETH), it becomes a lot less acurate on smaller markets with low volume and liquidity. -In order to backtest with 100% accuracy one would need the exact state of the orderbook (spread and depth) as well as information about orders happening around the time of each advice. With Gekko we made the decision to not store all this information (to simplify importing and storing market data). In voluminous and liquid markets this shouldn't be too much of a problem, but if you are backtesting over a small market (like some altcoins) the estimation will be of poor accuracy. +In live trading the notion of the "price" is more complicated than a single number. Both `spread` and `slippage` will effect your trade prices: these numbers describe your desired trades in relation to what people are currently offering in the market (this is called the orderbook). Read more about this in [this explanation](https://github.com/askmike/gekko/issues/2380#issuecomment-408744682). If you look at the following backtest result: diff --git a/docs/features/paper_trading.md b/docs/features/paper_trading.md new file mode 100644 index 000000000..fb734575a --- /dev/null +++ b/docs/features/paper_trading.md @@ -0,0 +1,7 @@ +# Paper trading + +Gekko can automatically run a strategy over the live markets and simulate in realtime what happen if you would have traded on its signals. Paper trading and [backtesting](../backtesting.md) are the two simulation modes that come with Gekko. It's a great way to experiment with strategies without putting your money on the line. + +You can start a paper trader by going to live gekkos and clicking on "Start a new live Gekko". + +Keep in mind that a paper trader is a simulation, and the accuracy depends on the market you decide to run it on (you'll get pretty accurate results on big markets like USD/BTC). You can read more about the details and limitations of the simulation on [the backtesting page](../backtesting.md#Simplified-simulation). \ No newline at end of file diff --git a/docs/features/trading_bot.md b/docs/features/trading_bot.md new file mode 100644 index 000000000..50860d5f0 --- /dev/null +++ b/docs/features/trading_bot.md @@ -0,0 +1,22 @@ +# Trading bot + +Once you have run enough simulations (using [backtesting](./hacktesting.md) and [paper trading](./paper_trading.md)) and you are confident in your strategy you can use Gekko as a trading bot. + +Gekko will run your strategy on the live market and automatically trade on your exchange account when trade signals come out of your strategy. + +## Preparation + +1. Make sure you are fully confident in your strategy! If you want to play around use either the [paper trader](./paper_trader.md) or the [backtester](./backtesting.md). Once you are confident continue with this list. +2. Gekko will need to have API keys to your exchange account that have permissions to view balances and orders and create new orders. Keep in mind: + - Gekko does NOT need withdrawal access, for your safety DO NOT create API keys that can withdraw. + - Make sure you only use the API key for Gekko, and for nothing else. + - If possible try to restrict the API key to the IP address you will run Gekko from (this makes moest sense in server environments) +3. Start your gekko through either the UI or the commandline interface! + +## Notes + +Gekko will trade on the market you configured that consists of two currencies (for example USD/BTC): + - Try to not trade either of these currencies on the account you use with Gekko. (in the example above: don't trade any USD nor any BTC). + - Try to not withdraw or deposit more of either of these currencies. + +While Gekko will handle the situations above, all the profit calculations will be incorrect since your balances are taken into account while calculating profits. \ No newline at end of file From 9c77a95c243022d7fc8a061d42c021d40f877aba Mon Sep 17 00:00:00 2001 From: Mike van Rossum Date: Sun, 12 Aug 2018 22:22:33 +0800 Subject: [PATCH 20/29] [DOCS] nits in trading bot doc --- docs/features/trading_bot.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/features/trading_bot.md b/docs/features/trading_bot.md index 50860d5f0..d716ee106 100644 --- a/docs/features/trading_bot.md +++ b/docs/features/trading_bot.md @@ -1,6 +1,6 @@ # Trading bot -Once you have run enough simulations (using [backtesting](./hacktesting.md) and [paper trading](./paper_trading.md)) and you are confident in your strategy you can use Gekko as a trading bot. +Once you have run enough simulations (using [backtesting](./backtesting.md) and [paper trading](./paper_trading.md)) and you are confident in your strategy you can use Gekko as a trading bot. Gekko will run your strategy on the live market and automatically trade on your exchange account when trade signals come out of your strategy. @@ -9,7 +9,7 @@ Gekko will run your strategy on the live market and automatically trade on your 1. Make sure you are fully confident in your strategy! If you want to play around use either the [paper trader](./paper_trader.md) or the [backtester](./backtesting.md). Once you are confident continue with this list. 2. Gekko will need to have API keys to your exchange account that have permissions to view balances and orders and create new orders. Keep in mind: - Gekko does NOT need withdrawal access, for your safety DO NOT create API keys that can withdraw. - - Make sure you only use the API key for Gekko, and for nothing else. + - Make sure you only use the API key for Gekko, and for nothing else. If in doubt create a new key (and remove stale ones). - If possible try to restrict the API key to the IP address you will run Gekko from (this makes moest sense in server environments) 3. Start your gekko through either the UI or the commandline interface! From 2bc2945e22e1324c30774ae6235ecb8bd8677c83 Mon Sep 17 00:00:00 2001 From: Mike van Rossum Date: Mon, 13 Aug 2018 16:01:58 +0800 Subject: [PATCH 21/29] make sure we propagate summary error, fix #2439 --- plugins/trader/trader.js | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/plugins/trader/trader.js b/plugins/trader/trader.js index 2ce53b198..d4bb51405 100644 --- a/plugins/trader/trader.js +++ b/plugins/trader/trader.js @@ -265,6 +265,20 @@ Trader.prototype.createOrder = function(side, amount, advice, id) { }); this.order.on('completed', () => { this.order.createSummary((err, summary) => { + if(!err && !summary) { + err = new Error('GB returned an empty summary.') + } + + if(err) { + log.error('Error while creating summary:', err); + return this.deferredEmit('tradeErrored', { + id, + adviceId: advice.id, + date: moment(), + reason: err.message + }); + } + log.info('[ORDER] summary:', summary); this.order = null; this.sync(() => { From 83ad97f17dae2d3664d9fa32367910173d979d80 Mon Sep 17 00:00:00 2001 From: Mike van Rossum Date: Mon, 13 Aug 2018 16:41:39 +0800 Subject: [PATCH 22/29] [GB] recheck the order after cancel failr, fix #2440 --- exchange/wrappers/coinfalcon.js | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/exchange/wrappers/coinfalcon.js b/exchange/wrappers/coinfalcon.js index 92f82594a..ed498cdb5 100644 --- a/exchange/wrappers/coinfalcon.js +++ b/exchange/wrappers/coinfalcon.js @@ -244,7 +244,22 @@ Trader.prototype.cancelOrder = function(order, callback) { const handle = this.processResponse(this.cancelOrder, args, (err, res) => { if(err) { if(err.message.includes('has wrong status.')) { - return callback(undefined, true); + + // see https://github.com/askmike/gekko/issues/2440 + console.log('CANCELFIX', order, 'order has wrong status...'); + return this.checkOrder(order, (err, res) => { + console.log('CANCELFIX', order, 'checked it:', res); + + if(err) { + return callback(err); + } + + if(!res.open) { + return callback(undefined, true); + } + + return this.cancelOrder(order, callback); + }); } return callback(err); } From 998671ef608815bbf5a747f3e39e52487a3875f0 Mon Sep 17 00:00:00 2001 From: Mike van Rossum Date: Mon, 13 Aug 2018 17:17:42 +0800 Subject: [PATCH 23/29] [GB] make sure sticky properly handles errors everywhere --- exchange/orders/sticky.js | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/exchange/orders/sticky.js b/exchange/orders/sticky.js index 385f27376..b1ebd44b9 100644 --- a/exchange/orders/sticky.js +++ b/exchange/orders/sticky.js @@ -199,8 +199,9 @@ class StickyOrder extends BaseOrder { } this.api.getTicker((err, ticker) => { - if(err) - throw err; + if(this.handleError(err)) { + return; + } this.ticker = ticker; this.emit('ticker', ticker); @@ -293,6 +294,10 @@ class StickyOrder extends BaseOrder { this.emitStatus(); this.api.cancelOrder(this.id, (err, filled, data) => { + if(this.handleError(err)) { + return; + } + // it got filled before we could cancel if(this.handleCancel(filled, data)) { return; @@ -395,7 +400,7 @@ class StickyOrder extends BaseOrder { this.filled(); return false; } else { - throw new Error("The amount " + this.amount + " is too small."); + this.handleError(new Error("The amount " + this.amount + " is too small.")); } } @@ -405,8 +410,8 @@ class StickyOrder extends BaseOrder { this.sticking = true; this.api.cancelOrder(this.id, (err, filled, data) => { - if(err) { - throw err; + if(this.handleError(err)) { + return; } // it got filled before we could cancel @@ -436,8 +441,8 @@ class StickyOrder extends BaseOrder { this.completing = true; clearTimeout(this.timeout); this.api.cancelOrder(this.id, (err, filled, data) => { - if(err) { - throw err; + if(this.handleError(err)) { + return; } this.cancelling = false; From a683bf675dfd33b89450c5c786a33ee44d931a16 Mon Sep 17 00:00:00 2001 From: Mike van Rossum Date: Mon, 13 Aug 2018 17:45:51 +0800 Subject: [PATCH 24/29] sell full balance, see & fix #2434 --- plugins/trader/trader.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/trader/trader.js b/plugins/trader/trader.js index d4bb51405..cc1139c48 100644 --- a/plugins/trader/trader.js +++ b/plugins/trader/trader.js @@ -200,7 +200,7 @@ Trader.prototype.processAdvice = function(advice) { }); } - amount = this.portfolio.asset * 0.95; + amount = this.portfolio.asset; log.info( 'Trader', From 4ed5687196e5d328421864319c0806dd881ad51a Mon Sep 17 00:00:00 2001 From: Mike van Rossum Date: Mon, 13 Aug 2018 18:13:51 +0800 Subject: [PATCH 25/29] [DOC] add youtube video link to intro doc --- docs/introduction/about_gekko.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/introduction/about_gekko.md b/docs/introduction/about_gekko.md index 8531fff6f..168b6c6c1 100644 --- a/docs/introduction/about_gekko.md +++ b/docs/introduction/about_gekko.md @@ -20,6 +20,10 @@ You can either create your own trading strategy or start with the built-in examp All the above modes can be run from the user interface, this interface will show charts and performance/risk statistics. +Here is a video that explains Gekko's core concepts: + +[![youtube video explaining gekko](https://gekko.wizb.it/_static/yt-gist.jpg)](https://www.youtube.com/watch?v=PKIxZ-Qaphk) + ## Strategies Gekko comes with some [example strategies](../strategies/introduction.md) (which implement a single indicator). But with some basic javascript you can [create your own strategies](../strategies/creating_a_strategy.md). You can use over 130 indicators to create your perfect prediction model (using [Talib's indicators](../strategies/talib_indicators.md) or [Tulip's indicators](../strategies/tulip_indicators.md)). *Why don't you combine Bollinger Bands, CCI and MACD with a STOCHRSI indicator?* From 3f8e26d63175847729e6b9d9ca4f9d52ea26e562 Mon Sep 17 00:00:00 2001 From: Mike van Rossum Date: Mon, 13 Aug 2018 22:22:38 +0800 Subject: [PATCH 26/29] [GB] make sure we defer cancelfix API calls --- exchange/wrappers/coinfalcon.js | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/exchange/wrappers/coinfalcon.js b/exchange/wrappers/coinfalcon.js index ed498cdb5..e51df65d8 100644 --- a/exchange/wrappers/coinfalcon.js +++ b/exchange/wrappers/coinfalcon.js @@ -247,19 +247,24 @@ Trader.prototype.cancelOrder = function(order, callback) { // see https://github.com/askmike/gekko/issues/2440 console.log('CANCELFIX', order, 'order has wrong status...'); - return this.checkOrder(order, (err, res) => { - console.log('CANCELFIX', order, 'checked it:', res); - - if(err) { - return callback(err); - } - - if(!res.open) { - return callback(undefined, true); - } - - return this.cancelOrder(order, callback); - }); + return setTimeout(() => { + this.checkOrder(order, (err, res) => { + console.log('CANCELFIX', order, 'checked it:', res); + + if(err) { + return callback(err); + } + + if(!res.open) { + return callback(undefined, true); + } + + return setTimeout( + () => this.cancelOrder(order, callback), + this.interval + ); + }); + }, this.interval); } return callback(err); } From a3efa7eb5272beba37a5193800f3e3e0abe52901 Mon Sep 17 00:00:00 2001 From: Mike van Rossum Date: Tue, 14 Aug 2018 13:59:27 +0800 Subject: [PATCH 27/29] [DOCS] add internal/plugin doc back --- docs/internals/plugins.md | 41 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 docs/internals/plugins.md diff --git a/docs/internals/plugins.md b/docs/internals/plugins.md new file mode 100644 index 000000000..ad03b0305 --- /dev/null +++ b/docs/internals/plugins.md @@ -0,0 +1,41 @@ +# Plugins + +*This is a technical document explaining the role of Plugins in the Gekko codebase. For a non technical document about available plugins, see [here](../commandline/plugins.md)* + +Within Gekko most functionality is encapsulated into "plugins". These are simple modules that process some data (from [Gekko events](./events.md)) and do something with it. For example emitting a new event or sending a message out to some external service like telegram, or doing a live trade at an exchange. + +Whenever you run a Gekko (live trader, paper trader, backtester or importer) you are simply running a number of plugins and feeding them with market data. For a more detailed explanation, see the [architecture doc](./architecture.md). + +For example, there is a plugin called the paperTrader which is responsible for simulating trades (used in [backtests](../features/backtesting.md) and [paper trading](../features/paper_trading.md)). It does this by listening to advice events coming from a strategy, and simulating trades whenever they fire (and firing trade events). + +- All plugins are javascript files that expose a constructor function (or ES6 class). +- All plugins are stored in `gekko/plugins`. +- All plugins are described in the file [`gekko/plugins.js`](https://github.com/askmike/gekko/blob/develop/plugins.js), this way Gekko knows how/when the plugin can be used (for example it does not make sense to run a telegram bot that will emit all trades in a backtest). + +## Structure of a plugin + +A plugin can be a very simple module that simply listens to some event: + + + // A plugin that will buy Champagne when we MOON + + const _ = require('lodash'); + + // example: doesn't actually work.. + const alexa = require('alexa'); + + const MOON = 1000000; + + const Plugin = function() { + _.bindAll(this); + } + + Plugin.prototype.processPortfolioValueChange = function(event) { + if(event.value > MOON) { + alexa.say('Alexa, buy the best Champagne!'); + } + }; + + module.exports = Plugin; + +Have a look at the [events doc](./events.md) for all events your plugin can subscribe to. \ No newline at end of file From 5e67cf0f068186c4c7e7dd9514ac1eae552ce341 Mon Sep 17 00:00:00 2001 From: Mike van Rossum Date: Tue, 14 Aug 2018 13:59:35 +0800 Subject: [PATCH 28/29] [DOCS] fix broken links --- docs/commandline/plugins.md | 1 + docs/features/paper_trading.md | 2 +- docs/internals/events.md | 2 +- docs/internals/plugins.md | 4 ++-- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/docs/commandline/plugins.md b/docs/commandline/plugins.md index 4da153580..40e2bf4d1 100644 --- a/docs/commandline/plugins.md +++ b/docs/commandline/plugins.md @@ -13,6 +13,7 @@ Gekko currently has a couple plugins: - Campfire bot - Redis beacon - XMP Bot +- and more.. To configure a plugin, open up your `config.js` file with a text editor and configure the appropiate section. diff --git a/docs/features/paper_trading.md b/docs/features/paper_trading.md index fb734575a..53355dbf3 100644 --- a/docs/features/paper_trading.md +++ b/docs/features/paper_trading.md @@ -4,4 +4,4 @@ Gekko can automatically run a strategy over the live markets and simulate in rea You can start a paper trader by going to live gekkos and clicking on "Start a new live Gekko". -Keep in mind that a paper trader is a simulation, and the accuracy depends on the market you decide to run it on (you'll get pretty accurate results on big markets like USD/BTC). You can read more about the details and limitations of the simulation on [the backtesting page](../backtesting.md#Simplified-simulation). \ No newline at end of file +Keep in mind that a paper trader is a simulation, and the accuracy depends on the market you decide to run it on (you'll get pretty accurate results on big markets like USD/BTC). You can read more about the details and limitations of the simulation on [the backtesting page](./backtesting.md#Simplified-simulation). \ No newline at end of file diff --git a/docs/internals/events.md b/docs/internals/events.md index 2b8a3612b..e5686f791 100644 --- a/docs/internals/events.md +++ b/docs/internals/events.md @@ -1,6 +1,6 @@ # Events -As described in the [architecture](./architecture.md) events play a key role in the complete system: they relay all information between separate components (like plugins). This makes the codebase scalable, testable and it separates concerns. +As described in the [architecture](./architecture.md) events play a key role in the complete system: they relay all information between separate components (like [plugins](./plugins.md)). This makes the codebase scalable, testable and it separates concerns. if you run the Gekko UI events are relayed between core components as well as broadcasted (via the UI server) to the web UI. This means that all events broadcasted by any plugin automatically end up in the web UI. diff --git a/docs/internals/plugins.md b/docs/internals/plugins.md index ad03b0305..9d9d94664 100644 --- a/docs/internals/plugins.md +++ b/docs/internals/plugins.md @@ -6,7 +6,7 @@ Within Gekko most functionality is encapsulated into "plugins". These are simple Whenever you run a Gekko (live trader, paper trader, backtester or importer) you are simply running a number of plugins and feeding them with market data. For a more detailed explanation, see the [architecture doc](./architecture.md). -For example, there is a plugin called the paperTrader which is responsible for simulating trades (used in [backtests](../features/backtesting.md) and [paper trading](../features/paper_trading.md)). It does this by listening to advice events coming from a strategy, and simulating trades whenever they fire (and firing trade events). +For example, there is a plugin called the paperTrader which is responsible for simulating trades (used in [backtests](../features/backtesting.md) and [paper trading](../features/paper_trading.md)). It does this by listening to advice events coming from a strategy, and simulating trades whenever they fire (and firing trade events). Find a longer list of plugins that come with Gekko [here](../commandline/plugins.md). - All plugins are javascript files that expose a constructor function (or ES6 class). - All plugins are stored in `gekko/plugins`. @@ -38,4 +38,4 @@ A plugin can be a very simple module that simply listens to some event: module.exports = Plugin; -Have a look at the [events doc](./events.md) for all events your plugin can subscribe to. \ No newline at end of file +Have a look at the [events doc](./events.md) for all events your plugin can subscribe to. For technical inspiration it's easiest to look at the code of Gekko's plugins (here [`gekko/plugins.js`](https://github.com/askmike/gekko/blob/develop/plugins.js)). \ No newline at end of file From 72a870fd68d337fb007e476463bac111745288ec Mon Sep 17 00:00:00 2001 From: Mike van Rossum Date: Tue, 14 Aug 2018 14:02:09 +0800 Subject: [PATCH 29/29] [DOCS] simplify plugin example --- docs/internals/plugins.md | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/docs/internals/plugins.md b/docs/internals/plugins.md index 9d9d94664..8b5d5ba66 100644 --- a/docs/internals/plugins.md +++ b/docs/internals/plugins.md @@ -1,6 +1,6 @@ # Plugins -*This is a technical document explaining the role of Plugins in the Gekko codebase. For a non technical document about available plugins, see [here](../commandline/plugins.md)* +*This is a technical document explaining the role of Plugins in the Gekko codebase. For a non technical document about available plugins, see [here](../commandline/plugins.md).* Within Gekko most functionality is encapsulated into "plugins". These are simple modules that process some data (from [Gekko events](./events.md)) and do something with it. For example emitting a new event or sending a message out to some external service like telegram, or doing a live trade at an exchange. @@ -19,16 +19,12 @@ A plugin can be a very simple module that simply listens to some event: // A plugin that will buy Champagne when we MOON - const _ = require('lodash'); - // example: doesn't actually work.. const alexa = require('alexa'); const MOON = 1000000; - const Plugin = function() { - _.bindAll(this); - } + const Plugin = function() {} Plugin.prototype.processPortfolioValueChange = function(event) { if(event.value > MOON) {