From aa7869eb3380557803398e9d31400a9be826e47d Mon Sep 17 00:00:00 2001 From: Francois Klieber Date: Sun, 6 Jun 2021 20:25:14 +0200 Subject: [PATCH] first commit --- .dockerignore | 7 + .drone.yml | 16 + .editorconfig | 9 + .env | 3 + .github/ISSUE_TEMPLATE.md | 17 + .github/PULL_REQUEST_TEMPLATE.md | 13 + .github/stale.yml | 19 + .gitignore | 52 + .prettierrc | 4 + .travis.yml | 3 + CONTRIBUTING.md | 48 + Dockerfile | 34 + LICENSE | 9 + README.md | 43 + appveyor.yml | 15 + config/adapters/mongodb.toml | 7 + config/adapters/postgresql.toml | 7 + config/adapters/sqlite.toml | 7 + config/backtest.toml | 8 + config/general.toml | 10 + config/plugins/candleWriter.toml | 2 + config/plugins/paperTrader.toml | 8 + config/plugins/performanceAnalyzer.toml | 5 + config/plugins/pushbullet.toml | 16 + config/plugins/trader.toml | 9 + config/plugins/tradingAdvisor.toml | 9 + config/strategies/CCI.toml | 10 + config/strategies/DEMA.toml | 5 + config/strategies/MACD.toml | 8 + config/strategies/PPO.toml | 8 + config/strategies/RSI.toml | 6 + config/strategies/StochRSI.toml | 6 + config/strategies/TMA.toml | 3 + config/strategies/TSI.toml | 7 + config/strategies/UO.toml | 16 + config/strategies/custom.toml | 1 + config/strategies/talib-macd.toml | 8 + config/strategies/tulip-adx.toml | 7 + config/strategies/tulip-macd.toml | 8 + config/strategies/tulip-multi-strat.toml | 12 + config/strategies/varPPO.toml | 9 + core/budfox/budfox.js | 78 + core/budfox/candleCreator.js | 200 + core/budfox/candleManager.js | 34 + core/budfox/heart.js | 51 + core/budfox/marketDataProvider.js | 45 + core/budfox/marketFetcher.js | 109 + core/budfox/tradeBatcher.js | 124 + core/candleBatcher.js | 93 + core/emitter.js | 34 + core/gekkoStream.js | 121 + core/log.js | 81 + core/markets/backtest.js | 108 + core/markets/importer.js | 117 + core/markets/leech.js | 91 + core/markets/realtime.js | 19 + core/pipeline.js | 252 + core/pluginUtil.js | 120 + core/prepareDateRange.js | 67 + core/stats.js | 17 + core/talib.js | 2297 +++ core/tools/candleLoader.js | 100 + core/tools/configBuilder.js | 42 + core/tools/dataStitcher.js | 233 + core/tools/dateRangeScanner.js | 138 + core/tulind.js | 1002 ++ core/util.js | 189 + core/workers/datasetScan/parent.js | 54 + core/workers/dateRangeScan/child.js | 26 + core/workers/dateRangeScan/parent.js | 21 + core/workers/loadCandles/child.js | 28 + core/workers/loadCandles/parent.js | 68 + core/workers/pipeline/child.js | 66 + .../messageHandlers/backtestHandler.js | 25 + .../messageHandlers/importerHandler.js | 26 + .../messageHandlers/realtimeHandler.js | 24 + core/workers/pipeline/parent.js | 34 + docker-compose.yml | 46 + docker-entrypoint.sh | 9 + docs/commandline/Importing.md | 41 + docs/commandline/about_the_commandline.md | 28 + docs/commandline/backtesting.md | 71 + docs/commandline/plugins.md | 214 + docs/commandline/tradebot.md | 25 + docs/extending/add_a_plugin.md | 38 + docs/extending/add_an_exchange.md | 181 + docs/extending/other_software.md | 21 + docs/features/backtesting.md | 23 + docs/features/importing.md | 11 + docs/features/paper_trading.md | 7 + docs/features/trading_bot.md | 22 + docs/gekko-broker/introduction.md | 130 + docs/gekko-broker/sticky_order.md | 48 + docs/gekko-broker/wrapper_api.md | 22 + .../configuring_gekko_on_a_server.md | 148 + docs/installation/installing_gekko.md | 62 + ...installing_gekko_on_raspberry_pi_2_or_3.md | 74 + .../installing_gekko_on_windows.md | 77 + ...ekko_on_windows_with_bash_on_windows_10.md | 91 + .../installing_gekko_using_docker.md | 25 + docs/installation/updating_gekko.md | 42 + docs/internals/architecture.md | 44 + docs/internals/budfox.md | 41 + docs/internals/events.md | 359 + docs/internals/gekko_ui.md | 57 + docs/internals/plugins.md | 37 + docs/internals/server_api.md | 795 + docs/introduction/about_gekko.md | 80 + docs/introduction/getting_help.md | 25 + docs/introduction/roadmap.md | 12 + docs/introduction/scope.md | 30 + docs/introduction/supported_exchanges.md | 66 + docs/introduction/supporting_the_project.md | 8 + docs/strategies/creating_a_strategy.md | 199 + docs/strategies/gekko_indicators.md | 122 + docs/strategies/introduction.md | 193 + docs/strategies/talib_indicators.md | 609 + docs/strategies/tulip_indicators.md | 709 + exchange/.npmignore | 1 + exchange/README.md | 3 + exchange/dependencyCheck.js | 23 + exchange/exchangeChecker.js | 104 + exchange/exchangeErrors.js | 42 + exchange/exchangeUtils.js | 154 + exchange/gekkoBroker.js | 180 + exchange/orders/index.js | 5 + exchange/orders/limit.js | 244 + exchange/orders/order.js | 88 + exchange/orders/states.js | 34 + exchange/orders/sticky.js | 754 + exchange/package-lock.json | 823 + exchange/package.json | 40 + exchange/portfolioManager.js | 87 + exchange/trigger.js | 80 + exchange/triggers/index.js | 3 + exchange/triggers/trailingStop.js | 62 + .../util/genMarketFiles/update-binance.js | 51 + .../util/genMarketFiles/update-bitfinex.js | 54 + .../util/genMarketFiles/update-coinbase.js | 54 + .../util/genMarketFiles/update-coinfalcon.js | 45 + exchange/util/genMarketFiles/update-kraken.js | 152 + exchange/wrappers/DEBUG_exchange-simulator.js | 72 + exchange/wrappers/binance-markets.json | 4599 ++++++ exchange/wrappers/binance.js | 530 + exchange/wrappers/bitcoin-co-id.js.old | 296 + exchange/wrappers/bitfinex-markets.json | 3264 ++++ exchange/wrappers/bitfinex.js | 319 + exchange/wrappers/bitfinex_v2.js.prep | 246 + exchange/wrappers/bitstamp.js.old | 261 + exchange/wrappers/bittrex.js | 633 + exchange/wrappers/btc-markets.js.old | 249 + exchange/wrappers/btcc.js.old | 176 + exchange/wrappers/bx.in.th.js.old | 89 + exchange/wrappers/cexio.js.old | 211 + exchange/wrappers/coinbase-markets.json | 238 + exchange/wrappers/coinfalcon-markets.json | 316 + exchange/wrappers/coinfalcon.js | 385 + exchange/wrappers/coingi.js | 283 + exchange/wrappers/exmo-markets.json | 43 + exchange/wrappers/exmo.js | 239 + exchange/wrappers/gdax.js | 430 + exchange/wrappers/gemini.js.old | 228 + exchange/wrappers/kraken-markets.json | 1259 ++ exchange/wrappers/kraken.js | 471 + exchange/wrappers/lakebtc.js | 138 + exchange/wrappers/luno.js | 416 + exchange/wrappers/mexbt.js.old | 154 + exchange/wrappers/mtgox.js.old | 214 + exchange/wrappers/okcoin.js.old | 194 + exchange/wrappers/poloniex-markets.json | 3257 ++++ exchange/wrappers/poloniex.js | 533 + exchange/wrappers/quadriga-markets.json | 136 + exchange/wrappers/quadriga.js.old | 251 + exchange/wrappers/therocktrading-markets.json | 187 + exchange/wrappers/therocktrading.js | 286 + exchange/wrappers/wex.nz.js.old | 301 + exchange/wrappers/zaif.jp.js.old | 144 + gekko.js | 63 + importers/exchanges/binance.js | 60 + importers/exchanges/bitfinex.js | 148 + importers/exchanges/btcc.js | 89 + importers/exchanges/coinfalcon.js | 53 + importers/exchanges/gdax.js | 168 + importers/exchanges/kraken.js | 81 + importers/exchanges/luno.js | 111 + importers/exchanges/poloniex.js | 126 + importers/exchanges/therocktrading.js | 91 + logs/.gitignore | 2 + package-lock.json | 1826 +++ package.json | 60 + plugins.js | 260 + plugins/adviceLogger.js | 36 + plugins/backtestResultExporter.js | 146 + plugins/blotter.js | 118 + plugins/campfire.js | 132 + plugins/candleUploader.js | 77 + plugins/childToParent.js | 24 + plugins/eventLogger.js | 20 + plugins/ifttt.js | 88 + plugins/ircbot.js | 154 + plugins/kodi.js | 68 + plugins/mailer.js | 138 + plugins/mongodb/handle.js | 47 + plugins/mongodb/reader.js | 120 + plugins/mongodb/scanner.js | 40 + plugins/mongodb/util.js | 15 + plugins/mongodb/writer.js | 115 + plugins/paperTrader/paperTrader.js | 283 + plugins/performanceAnalyzer/logger.js | 134 + .../performanceAnalyzer.js | 251 + plugins/postgresql/handle.js | 114 + plugins/postgresql/reader.js | 209 + plugins/postgresql/scanner.js | 93 + plugins/postgresql/util.js | 66 + plugins/postgresql/writer.js | 73 + plugins/pushbullet.js | 311 + plugins/pushover.js | 85 + plugins/redisBeacon.js | 67 + plugins/slack.js | 96 + plugins/sqlite/handle.js | 58 + plugins/sqlite/reader.js | 169 + plugins/sqlite/scanner.js | 64 + plugins/sqlite/util.js | 15 + plugins/sqlite/writer.js | 128 + plugins/telegrambot.js | 197 + plugins/trader/trader.js | 412 + .../tradingAdvisor/asyncIndicatorRunner.js | 150 + plugins/tradingAdvisor/baseTradingMethod.js | 334 + plugins/tradingAdvisor/tradingAdvisor.js | 122 + plugins/twitter.js | 84 + plugins/webserver.js | 27 + plugins/xmppbot.js | 193 + sample-config.js | 539 + strategies/CCI.js | 125 + strategies/DEBUG_single-advice.js | 37 + strategies/DEBUG_toggle-advice.js | 45 + strategies/DEMA.js | 73 + strategies/MACD.js | 134 + strategies/PPO.js | 148 + strategies/RSI.js | 105 + strategies/StochRSI.js | 121 + strategies/TMA.js | 34 + strategies/TSI.js | 98 + strategies/UO.js | 98 + strategies/custom.js | 63 + strategies/indicators/CCI.js | 75 + strategies/indicators/DEMA.js | 19 + strategies/indicators/EMA.js | 36 + strategies/indicators/LRC.js | 115 + strategies/indicators/MACD.js | 27 + strategies/indicators/PPO.js | 37 + strategies/indicators/RSI.js | 53 + strategies/indicators/SMA.js | 21 + strategies/indicators/SMMA.js | 28 + strategies/indicators/TSI.js | 38 + strategies/indicators/UO.js | 45 + strategies/noop.js | 13 + strategies/talib-macd.js | 59 + strategies/tulip-adx.js | 30 + strategies/tulip-macd.js | 58 + strategies/tulip-multi-strat.js | 57 + strategies/varPPO.js | 136 + subscriptions.js | 114 + test/_prepare.js | 5 + test/candleBatcher.js | 104 + test/exchanges/bitstamp.js | 114 + test/exchanges/data/bitstamp_trades.json | 947 ++ test/indicators/cci.js | 59 + test/indicators/dema.js | 38 + test/indicators/ema.js | 55 + test/indicators/macd.js | 100 + test/indicators/ppo.js | 55 + test/indicators/rsi.js | 53 + test/indicators/sma.js | 46 + test/indicators/smma.js | 53 + test/marketFetcher.js | 98 + test/plugins/portfolioManager.js | 0 test/test-config.json | 132 + test/tradeBatcher.js | 139 + test/triggers/trailingStop.js | 181 + web/apiKeyManager.js | 48 + web/baseUIconfig.js | 26 + web/isWindows.js | 9 + web/routes/apiKeys.js | 26 + web/routes/backtest.js | 31 + web/routes/baseConfig.js | 104 + web/routes/configPart.js | 20 + web/routes/deleteGekko.js | 22 + web/routes/exchanges.js | 39 + web/routes/getCandles.js | 40 + web/routes/import.js | 75 + web/routes/info.js | 8 + web/routes/list.js | 7 + web/routes/scanDatasets.js | 19 + web/routes/scanDateRange.js | 19 + web/routes/startGekko.js | 38 + web/routes/stopGekko.js | 23 + web/routes/strategies.js | 27 + web/server.js | 139 + web/state/cache.js | 14 + web/state/gekkoManager.js | 254 + web/state/listManager.js | 59 + web/state/logger.js | 38 + web/state/reduceState.js | 67 + web/vue/babel.config.js | 9 + web/vue/dist/UIconfig.js | 26 + web/vue/dist/app.5e99ecf7.js | 2 + web/vue/dist/app.5e99ecf7.js.map | 1 + web/vue/dist/app.730569ff.css | 1 + web/vue/dist/chunk-vendors.b9a11975.js | 26 + web/vue/dist/chunk-vendors.b9a11975.js.map | 1 + web/vue/dist/index.html | 1 + web/vue/dist/static/gekko.jpg | Bin 0 -> 37814 bytes web/vue/dist/vendor/d3.js | 8 + web/vue/dist/vendor/furtive.min.css | 162 + web/vue/dist/vendor/humanize-duration.js | 193 + web/vue/dist/vendor/moment.js | 548 + .../dist/vendor/reconnecting-websocket.min.js | 2 + web/vue/dist/vendor/select-arrow.png | Bin 0 -> 288 bytes web/vue/dist/vendor/toml.js | 2 + web/vue/package-lock.json | 13491 ++++++++++++++++ web/vue/package.json | 36 + web/vue/public/UIconfig.js | 26 + web/vue/public/index.html | 24 + web/vue/public/static/gekko.jpg | Bin 0 -> 37814 bytes web/vue/public/vendor/d3.js | 8 + web/vue/public/vendor/furtive.min.css | 162 + web/vue/public/vendor/humanize-duration.js | 193 + web/vue/public/vendor/moment.js | 548 + .../vendor/reconnecting-websocket.min.js | 2 + web/vue/public/vendor/select-arrow.png | Bin 0 -> 288 bytes web/vue/public/vendor/toml.js | 2 + web/vue/src/App.vue | 99 + .../backtester/backtestConfigBuilder.vue | 139 + .../src/components/backtester/backtester.vue | 55 + .../backtester/result/chartWrapper.vue | 104 + .../components/backtester/result/result.vue | 41 + .../backtester/result/roundtripTable.vue | 93 + .../components/backtester/result/summary.vue | 58 + .../components/config/apiConfigBuilder.vue | 96 + web/vue/src/components/config/config.vue | 58 + web/vue/src/components/data/data.vue | 103 + .../data/import/importConfigBuilder.vue | 65 + .../src/components/data/import/importer.vue | 90 + web/vue/src/components/data/import/single.vue | 110 + .../components/gekko/gekkoConfigBuilder.vue | 123 + web/vue/src/components/gekko/list.vue | 143 + web/vue/src/components/gekko/new.vue | 200 + web/vue/src/components/gekko/singleGekko.vue | 361 + .../src/components/gekko/singleWatcher.vue | 153 + .../src/components/global/blockSpinner.vue | 69 + .../global/configbuilder/datasetpicker.vue | 131 + .../global/configbuilder/exchangepicker.vue | 65 + .../global/configbuilder/marketpicker.vue | 102 + .../global/configbuilder/papertrader.vue | 72 + .../global/configbuilder/rangecreator.vue | 93 + .../global/configbuilder/rangepicker.vue | 138 + .../global/configbuilder/stratpicker.vue | 145 + .../global/configbuilder/typepicker.vue | 48 + .../src/components/global/mixins/dataset.js | 60 + .../components/global/paperTradeSummary.vue | 64 + web/vue/src/components/global/progressBar.vue | 57 + web/vue/src/components/global/ws.js | 60 + web/vue/src/components/layout/footer.vue | 23 + web/vue/src/components/layout/header.vue | 46 + web/vue/src/components/layout/home.vue | 34 + web/vue/src/components/layout/modal.vue | 80 + web/vue/src/d3/chart3.js | 149 + web/vue/src/d3/chart4.js | 204 + web/vue/src/d3/message.js | 11 + web/vue/src/main.js | 47 + web/vue/src/store/index.js | 38 + web/vue/src/store/init.js | 14 + web/vue/src/store/modules/config/mutations.js | 11 + web/vue/src/store/modules/config/sync.js | 54 + web/vue/src/store/modules/gekkos/mutations.js | 77 + web/vue/src/store/modules/gekkos/sync.js | 28 + .../src/store/modules/imports/mutations.js | 23 + web/vue/src/store/modules/imports/sync.js | 20 + .../src/store/modules/messages/mutations.js | 0 .../store/modules/notifications/mutations.js | 7 + .../src/store/modules/notifications/sync.js | 15 + web/vue/src/tools/ajax.js | 30 + web/vue/src/tools/api.js | 28 + web/vue/src/tools/marked.js | 24 + web/vue/vue.config.js | 19 + 386 files changed, 69375 insertions(+) create mode 100644 .dockerignore create mode 100644 .drone.yml create mode 100644 .editorconfig create mode 100644 .env create mode 100644 .github/ISSUE_TEMPLATE.md create mode 100644 .github/PULL_REQUEST_TEMPLATE.md create mode 100644 .github/stale.yml create mode 100644 .gitignore create mode 100644 .prettierrc create mode 100644 .travis.yml create mode 100644 CONTRIBUTING.md create mode 100644 Dockerfile create mode 100644 LICENSE create mode 100644 README.md create mode 100644 appveyor.yml create mode 100644 config/adapters/mongodb.toml create mode 100644 config/adapters/postgresql.toml create mode 100644 config/adapters/sqlite.toml create mode 100644 config/backtest.toml create mode 100644 config/general.toml create mode 100644 config/plugins/candleWriter.toml create mode 100644 config/plugins/paperTrader.toml create mode 100644 config/plugins/performanceAnalyzer.toml create mode 100644 config/plugins/pushbullet.toml create mode 100644 config/plugins/trader.toml create mode 100644 config/plugins/tradingAdvisor.toml create mode 100644 config/strategies/CCI.toml create mode 100644 config/strategies/DEMA.toml create mode 100644 config/strategies/MACD.toml create mode 100644 config/strategies/PPO.toml create mode 100644 config/strategies/RSI.toml create mode 100644 config/strategies/StochRSI.toml create mode 100644 config/strategies/TMA.toml create mode 100644 config/strategies/TSI.toml create mode 100644 config/strategies/UO.toml create mode 100644 config/strategies/custom.toml create mode 100644 config/strategies/talib-macd.toml create mode 100644 config/strategies/tulip-adx.toml create mode 100644 config/strategies/tulip-macd.toml create mode 100644 config/strategies/tulip-multi-strat.toml create mode 100644 config/strategies/varPPO.toml create mode 100644 core/budfox/budfox.js create mode 100644 core/budfox/candleCreator.js create mode 100644 core/budfox/candleManager.js create mode 100644 core/budfox/heart.js create mode 100644 core/budfox/marketDataProvider.js create mode 100644 core/budfox/marketFetcher.js create mode 100644 core/budfox/tradeBatcher.js create mode 100644 core/candleBatcher.js create mode 100644 core/emitter.js create mode 100644 core/gekkoStream.js create mode 100644 core/log.js create mode 100644 core/markets/backtest.js create mode 100644 core/markets/importer.js create mode 100644 core/markets/leech.js create mode 100644 core/markets/realtime.js create mode 100644 core/pipeline.js create mode 100644 core/pluginUtil.js create mode 100644 core/prepareDateRange.js create mode 100644 core/stats.js create mode 100644 core/talib.js create mode 100644 core/tools/candleLoader.js create mode 100644 core/tools/configBuilder.js create mode 100644 core/tools/dataStitcher.js create mode 100644 core/tools/dateRangeScanner.js create mode 100644 core/tulind.js create mode 100644 core/util.js create mode 100644 core/workers/datasetScan/parent.js create mode 100644 core/workers/dateRangeScan/child.js create mode 100644 core/workers/dateRangeScan/parent.js create mode 100644 core/workers/loadCandles/child.js create mode 100644 core/workers/loadCandles/parent.js create mode 100644 core/workers/pipeline/child.js create mode 100644 core/workers/pipeline/messageHandlers/backtestHandler.js create mode 100644 core/workers/pipeline/messageHandlers/importerHandler.js create mode 100644 core/workers/pipeline/messageHandlers/realtimeHandler.js create mode 100644 core/workers/pipeline/parent.js create mode 100644 docker-compose.yml create mode 100644 docker-entrypoint.sh create mode 100644 docs/commandline/Importing.md create mode 100644 docs/commandline/about_the_commandline.md create mode 100644 docs/commandline/backtesting.md create mode 100644 docs/commandline/plugins.md create mode 100644 docs/commandline/tradebot.md create mode 100644 docs/extending/add_a_plugin.md create mode 100644 docs/extending/add_an_exchange.md create mode 100644 docs/extending/other_software.md create mode 100644 docs/features/backtesting.md create mode 100644 docs/features/importing.md create mode 100644 docs/features/paper_trading.md create mode 100644 docs/features/trading_bot.md create mode 100644 docs/gekko-broker/introduction.md create mode 100644 docs/gekko-broker/sticky_order.md create mode 100644 docs/gekko-broker/wrapper_api.md create mode 100644 docs/installation/configuring_gekko_on_a_server.md create mode 100644 docs/installation/installing_gekko.md create mode 100644 docs/installation/installing_gekko_on_raspberry_pi_2_or_3.md create mode 100644 docs/installation/installing_gekko_on_windows.md create mode 100644 docs/installation/installing_gekko_on_windows_with_bash_on_windows_10.md create mode 100644 docs/installation/installing_gekko_using_docker.md create mode 100644 docs/installation/updating_gekko.md create mode 100644 docs/internals/architecture.md create mode 100644 docs/internals/budfox.md create mode 100644 docs/internals/events.md create mode 100644 docs/internals/gekko_ui.md create mode 100644 docs/internals/plugins.md create mode 100644 docs/internals/server_api.md create mode 100644 docs/introduction/about_gekko.md create mode 100644 docs/introduction/getting_help.md create mode 100644 docs/introduction/roadmap.md create mode 100644 docs/introduction/scope.md create mode 100644 docs/introduction/supported_exchanges.md create mode 100644 docs/introduction/supporting_the_project.md create mode 100644 docs/strategies/creating_a_strategy.md create mode 100644 docs/strategies/gekko_indicators.md create mode 100644 docs/strategies/introduction.md create mode 100644 docs/strategies/talib_indicators.md create mode 100644 docs/strategies/tulip_indicators.md create mode 100644 exchange/.npmignore create mode 100644 exchange/README.md create mode 100644 exchange/dependencyCheck.js create mode 100644 exchange/exchangeChecker.js create mode 100644 exchange/exchangeErrors.js create mode 100644 exchange/exchangeUtils.js create mode 100644 exchange/gekkoBroker.js create mode 100644 exchange/orders/index.js create mode 100644 exchange/orders/limit.js create mode 100644 exchange/orders/order.js create mode 100644 exchange/orders/states.js create mode 100644 exchange/orders/sticky.js create mode 100644 exchange/package-lock.json create mode 100644 exchange/package.json create mode 100644 exchange/portfolioManager.js create mode 100644 exchange/trigger.js create mode 100644 exchange/triggers/index.js create mode 100644 exchange/triggers/trailingStop.js create mode 100644 exchange/util/genMarketFiles/update-binance.js create mode 100644 exchange/util/genMarketFiles/update-bitfinex.js create mode 100644 exchange/util/genMarketFiles/update-coinbase.js create mode 100644 exchange/util/genMarketFiles/update-coinfalcon.js create mode 100644 exchange/util/genMarketFiles/update-kraken.js create mode 100644 exchange/wrappers/DEBUG_exchange-simulator.js create mode 100644 exchange/wrappers/binance-markets.json create mode 100644 exchange/wrappers/binance.js create mode 100644 exchange/wrappers/bitcoin-co-id.js.old create mode 100644 exchange/wrappers/bitfinex-markets.json create mode 100644 exchange/wrappers/bitfinex.js create mode 100644 exchange/wrappers/bitfinex_v2.js.prep create mode 100644 exchange/wrappers/bitstamp.js.old create mode 100644 exchange/wrappers/bittrex.js create mode 100644 exchange/wrappers/btc-markets.js.old create mode 100644 exchange/wrappers/btcc.js.old create mode 100644 exchange/wrappers/bx.in.th.js.old create mode 100644 exchange/wrappers/cexio.js.old create mode 100644 exchange/wrappers/coinbase-markets.json create mode 100644 exchange/wrappers/coinfalcon-markets.json create mode 100644 exchange/wrappers/coinfalcon.js create mode 100755 exchange/wrappers/coingi.js create mode 100644 exchange/wrappers/exmo-markets.json create mode 100644 exchange/wrappers/exmo.js create mode 100644 exchange/wrappers/gdax.js create mode 100644 exchange/wrappers/gemini.js.old create mode 100644 exchange/wrappers/kraken-markets.json create mode 100644 exchange/wrappers/kraken.js create mode 100755 exchange/wrappers/lakebtc.js create mode 100644 exchange/wrappers/luno.js create mode 100644 exchange/wrappers/mexbt.js.old create mode 100644 exchange/wrappers/mtgox.js.old create mode 100644 exchange/wrappers/okcoin.js.old create mode 100644 exchange/wrappers/poloniex-markets.json create mode 100644 exchange/wrappers/poloniex.js create mode 100644 exchange/wrappers/quadriga-markets.json create mode 100644 exchange/wrappers/quadriga.js.old create mode 100644 exchange/wrappers/therocktrading-markets.json create mode 100644 exchange/wrappers/therocktrading.js create mode 100644 exchange/wrappers/wex.nz.js.old create mode 100644 exchange/wrappers/zaif.jp.js.old create mode 100644 gekko.js create mode 100644 importers/exchanges/binance.js create mode 100644 importers/exchanges/bitfinex.js create mode 100644 importers/exchanges/btcc.js create mode 100644 importers/exchanges/coinfalcon.js create mode 100644 importers/exchanges/gdax.js create mode 100644 importers/exchanges/kraken.js create mode 100644 importers/exchanges/luno.js create mode 100644 importers/exchanges/poloniex.js create mode 100644 importers/exchanges/therocktrading.js create mode 100644 logs/.gitignore create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 plugins.js create mode 100644 plugins/adviceLogger.js create mode 100644 plugins/backtestResultExporter.js create mode 100644 plugins/blotter.js create mode 100644 plugins/campfire.js create mode 100644 plugins/candleUploader.js create mode 100644 plugins/childToParent.js create mode 100644 plugins/eventLogger.js create mode 100644 plugins/ifttt.js create mode 100644 plugins/ircbot.js create mode 100644 plugins/kodi.js create mode 100644 plugins/mailer.js create mode 100644 plugins/mongodb/handle.js create mode 100644 plugins/mongodb/reader.js create mode 100644 plugins/mongodb/scanner.js create mode 100644 plugins/mongodb/util.js create mode 100644 plugins/mongodb/writer.js create mode 100644 plugins/paperTrader/paperTrader.js create mode 100644 plugins/performanceAnalyzer/logger.js create mode 100644 plugins/performanceAnalyzer/performanceAnalyzer.js create mode 100755 plugins/postgresql/handle.js create mode 100755 plugins/postgresql/reader.js create mode 100644 plugins/postgresql/scanner.js create mode 100755 plugins/postgresql/util.js create mode 100755 plugins/postgresql/writer.js create mode 100644 plugins/pushbullet.js create mode 100644 plugins/pushover.js create mode 100644 plugins/redisBeacon.js create mode 100644 plugins/slack.js create mode 100644 plugins/sqlite/handle.js create mode 100644 plugins/sqlite/reader.js create mode 100644 plugins/sqlite/scanner.js create mode 100644 plugins/sqlite/util.js create mode 100644 plugins/sqlite/writer.js create mode 100644 plugins/telegrambot.js create mode 100644 plugins/trader/trader.js create mode 100644 plugins/tradingAdvisor/asyncIndicatorRunner.js create mode 100644 plugins/tradingAdvisor/baseTradingMethod.js create mode 100644 plugins/tradingAdvisor/tradingAdvisor.js create mode 100644 plugins/twitter.js create mode 100644 plugins/webserver.js create mode 100644 plugins/xmppbot.js create mode 100644 sample-config.js create mode 100644 strategies/CCI.js create mode 100644 strategies/DEBUG_single-advice.js create mode 100644 strategies/DEBUG_toggle-advice.js create mode 100644 strategies/DEMA.js create mode 100644 strategies/MACD.js create mode 100644 strategies/PPO.js create mode 100644 strategies/RSI.js create mode 100644 strategies/StochRSI.js create mode 100644 strategies/TMA.js create mode 100644 strategies/TSI.js create mode 100644 strategies/UO.js create mode 100644 strategies/custom.js create mode 100644 strategies/indicators/CCI.js create mode 100644 strategies/indicators/DEMA.js create mode 100644 strategies/indicators/EMA.js create mode 100644 strategies/indicators/LRC.js create mode 100644 strategies/indicators/MACD.js create mode 100644 strategies/indicators/PPO.js create mode 100644 strategies/indicators/RSI.js create mode 100644 strategies/indicators/SMA.js create mode 100644 strategies/indicators/SMMA.js create mode 100644 strategies/indicators/TSI.js create mode 100644 strategies/indicators/UO.js create mode 100644 strategies/noop.js create mode 100644 strategies/talib-macd.js create mode 100644 strategies/tulip-adx.js create mode 100644 strategies/tulip-macd.js create mode 100644 strategies/tulip-multi-strat.js create mode 100644 strategies/varPPO.js create mode 100644 subscriptions.js create mode 100644 test/_prepare.js create mode 100644 test/candleBatcher.js create mode 100644 test/exchanges/bitstamp.js create mode 100644 test/exchanges/data/bitstamp_trades.json create mode 100644 test/indicators/cci.js create mode 100644 test/indicators/dema.js create mode 100644 test/indicators/ema.js create mode 100644 test/indicators/macd.js create mode 100644 test/indicators/ppo.js create mode 100644 test/indicators/rsi.js create mode 100644 test/indicators/sma.js create mode 100644 test/indicators/smma.js create mode 100644 test/marketFetcher.js create mode 100644 test/plugins/portfolioManager.js create mode 100644 test/test-config.json create mode 100644 test/tradeBatcher.js create mode 100644 test/triggers/trailingStop.js create mode 100644 web/apiKeyManager.js create mode 100644 web/baseUIconfig.js create mode 100644 web/isWindows.js create mode 100644 web/routes/apiKeys.js create mode 100644 web/routes/backtest.js create mode 100644 web/routes/baseConfig.js create mode 100644 web/routes/configPart.js create mode 100644 web/routes/deleteGekko.js create mode 100644 web/routes/exchanges.js create mode 100644 web/routes/getCandles.js create mode 100644 web/routes/import.js create mode 100644 web/routes/info.js create mode 100644 web/routes/list.js create mode 100644 web/routes/scanDatasets.js create mode 100644 web/routes/scanDateRange.js create mode 100644 web/routes/startGekko.js create mode 100644 web/routes/stopGekko.js create mode 100644 web/routes/strategies.js create mode 100644 web/server.js create mode 100644 web/state/cache.js create mode 100644 web/state/gekkoManager.js create mode 100644 web/state/listManager.js create mode 100644 web/state/logger.js create mode 100644 web/state/reduceState.js create mode 100644 web/vue/babel.config.js create mode 100644 web/vue/dist/UIconfig.js create mode 100644 web/vue/dist/app.5e99ecf7.js create mode 100644 web/vue/dist/app.5e99ecf7.js.map create mode 100644 web/vue/dist/app.730569ff.css create mode 100644 web/vue/dist/chunk-vendors.b9a11975.js create mode 100644 web/vue/dist/chunk-vendors.b9a11975.js.map create mode 100644 web/vue/dist/index.html create mode 100644 web/vue/dist/static/gekko.jpg create mode 100644 web/vue/dist/vendor/d3.js create mode 100644 web/vue/dist/vendor/furtive.min.css create mode 100644 web/vue/dist/vendor/humanize-duration.js create mode 100644 web/vue/dist/vendor/moment.js create mode 100644 web/vue/dist/vendor/reconnecting-websocket.min.js create mode 100644 web/vue/dist/vendor/select-arrow.png create mode 100644 web/vue/dist/vendor/toml.js create mode 100644 web/vue/package-lock.json create mode 100644 web/vue/package.json create mode 100644 web/vue/public/UIconfig.js create mode 100644 web/vue/public/index.html create mode 100644 web/vue/public/static/gekko.jpg create mode 100644 web/vue/public/vendor/d3.js create mode 100644 web/vue/public/vendor/furtive.min.css create mode 100644 web/vue/public/vendor/humanize-duration.js create mode 100644 web/vue/public/vendor/moment.js create mode 100644 web/vue/public/vendor/reconnecting-websocket.min.js create mode 100644 web/vue/public/vendor/select-arrow.png create mode 100644 web/vue/public/vendor/toml.js create mode 100644 web/vue/src/App.vue create mode 100644 web/vue/src/components/backtester/backtestConfigBuilder.vue create mode 100644 web/vue/src/components/backtester/backtester.vue create mode 100644 web/vue/src/components/backtester/result/chartWrapper.vue create mode 100644 web/vue/src/components/backtester/result/result.vue create mode 100644 web/vue/src/components/backtester/result/roundtripTable.vue create mode 100644 web/vue/src/components/backtester/result/summary.vue create mode 100644 web/vue/src/components/config/apiConfigBuilder.vue create mode 100644 web/vue/src/components/config/config.vue create mode 100644 web/vue/src/components/data/data.vue create mode 100644 web/vue/src/components/data/import/importConfigBuilder.vue create mode 100644 web/vue/src/components/data/import/importer.vue create mode 100644 web/vue/src/components/data/import/single.vue create mode 100644 web/vue/src/components/gekko/gekkoConfigBuilder.vue create mode 100644 web/vue/src/components/gekko/list.vue create mode 100644 web/vue/src/components/gekko/new.vue create mode 100644 web/vue/src/components/gekko/singleGekko.vue create mode 100644 web/vue/src/components/gekko/singleWatcher.vue create mode 100644 web/vue/src/components/global/blockSpinner.vue create mode 100644 web/vue/src/components/global/configbuilder/datasetpicker.vue create mode 100644 web/vue/src/components/global/configbuilder/exchangepicker.vue create mode 100644 web/vue/src/components/global/configbuilder/marketpicker.vue create mode 100644 web/vue/src/components/global/configbuilder/papertrader.vue create mode 100644 web/vue/src/components/global/configbuilder/rangecreator.vue create mode 100644 web/vue/src/components/global/configbuilder/rangepicker.vue create mode 100644 web/vue/src/components/global/configbuilder/stratpicker.vue create mode 100644 web/vue/src/components/global/configbuilder/typepicker.vue create mode 100644 web/vue/src/components/global/mixins/dataset.js create mode 100644 web/vue/src/components/global/paperTradeSummary.vue create mode 100644 web/vue/src/components/global/progressBar.vue create mode 100644 web/vue/src/components/global/ws.js create mode 100644 web/vue/src/components/layout/footer.vue create mode 100644 web/vue/src/components/layout/header.vue create mode 100644 web/vue/src/components/layout/home.vue create mode 100644 web/vue/src/components/layout/modal.vue create mode 100644 web/vue/src/d3/chart3.js create mode 100644 web/vue/src/d3/chart4.js create mode 100644 web/vue/src/d3/message.js create mode 100644 web/vue/src/main.js create mode 100644 web/vue/src/store/index.js create mode 100644 web/vue/src/store/init.js create mode 100644 web/vue/src/store/modules/config/mutations.js create mode 100644 web/vue/src/store/modules/config/sync.js create mode 100644 web/vue/src/store/modules/gekkos/mutations.js create mode 100644 web/vue/src/store/modules/gekkos/sync.js create mode 100644 web/vue/src/store/modules/imports/mutations.js create mode 100644 web/vue/src/store/modules/imports/sync.js create mode 100644 web/vue/src/store/modules/messages/mutations.js create mode 100644 web/vue/src/store/modules/notifications/mutations.js create mode 100644 web/vue/src/store/modules/notifications/sync.js create mode 100644 web/vue/src/tools/ajax.js create mode 100644 web/vue/src/tools/api.js create mode 100644 web/vue/src/tools/marked.js create mode 100644 web/vue/vue.config.js diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..31e4c3c --- /dev/null +++ b/.dockerignore @@ -0,0 +1,7 @@ +node_modules +.travis.yml +volumes +config.js +sample-config.js +.git +.gitignore \ No newline at end of file diff --git a/.drone.yml b/.drone.yml new file mode 100644 index 0000000..8c0d39d --- /dev/null +++ b/.drone.yml @@ -0,0 +1,16 @@ +kind: pipeline +name: default + + +steps: +- name: publish to harbor frantchenco.page + image: plugins/docker + settings: + repo: harbor.frantchenco.page/library/gekko + dockerfile: dockerfile + username: admin + password: Harbor12345 + registry: harbor.frantchenco.page + auto_tag: true + tags: + - latest \ No newline at end of file diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..9d08a1a --- /dev/null +++ b/.editorconfig @@ -0,0 +1,9 @@ +root = true + +[*] +charset = utf-8 +indent_style = space +indent_size = 2 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true diff --git a/.env b/.env new file mode 100644 index 0000000..a715140 --- /dev/null +++ b/.env @@ -0,0 +1,3 @@ +HOST=localhost +PORT=3000 +USE_SSL=0 diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md new file mode 100644 index 0000000..0d97fef --- /dev/null +++ b/.github/ISSUE_TEMPLATE.md @@ -0,0 +1,17 @@ +**Note: this is the technical bug tracker, please use other platforms for getting support and starting a (non technical) discussion. See the [getting help page](https://gekko.wizb.it/docs/introduction/getting-help.html) for details.** + +**I'm submitting a ...** +[ ] bug report +[ ] question about the decisions made in the repository + +**Action taken** (what you did) + + +**Expected result** (what you hoped would happen) + + +**Actual result** (unexpected outcome) + + +**Other information** (e.g. detailed explanation, stacktraces, related issues, suggestions how to fix, links for us to have context, eg. stackoverflow, etc) + diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..caae6b1 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,13 @@ +* **What kind of change does this PR introduce?** (Bug fix, feature, docs update, ...) + + + +* **What is the current behavior?** (You can also link to an open issue here) + + + +* **What is the new behavior (if this is a feature change)?** + + + +* **Other information**: diff --git a/.github/stale.yml b/.github/stale.yml new file mode 100644 index 0000000..67dd1aa --- /dev/null +++ b/.github/stale.yml @@ -0,0 +1,19 @@ +# Number of days of inactivity before an issue becomes stale +daysUntilStale: 60 +# Number of days of inactivity before a stale issue is closed +daysUntilClose: 7 +# Issues with these labels will never be considered stale +exemptLabels: + - pinned + - security + - important +# Label to use when marking an issue as stale +staleLabel: wontfix +# Comment to post when marking an issue as stale. Set to `false` to disable +markComment: > + This issue has been automatically marked as stale because it has not had + recent activity. It will be closed if no further activity occurs. Thank you + for your contributions. If you feel this is very a important issue please + reach out the maintainer of this project directly via e-mail: gekko at mvr dot me. +# Comment to post when closing a stale issue. Set to `false` to disable +closeComment: false \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4143049 --- /dev/null +++ b/.gitignore @@ -0,0 +1,52 @@ +# Numerous always-ignore extensions +*.diff +*.err +*.orig +*.log +*.rej +*.swo +*.swp +*.vi +*~ +*.sass-cache + +# OS or Editor folders +.DS_Store +Thumbs.db +config.js +api-keys.js +.cache +.project +.settings +.tmproj +nbproject +*.sublime-project +*.sublime-workspace + +# Dreamweaver added files +_notes +dwsync.xml + +# Komodo +*.komodoproject +.komodotools + +# Espresso +*.esproj +*.espressostorage + +# Folders & files to ignore + +node_modules +candles.csv +cexio.db +history +TMP_* +.idea +volumes +config.js +config-*.js +private-*.js +private-*.toml +SECRET-api-keys.json +exchange/nusadua \ No newline at end of file diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..6a5577e --- /dev/null +++ b/.prettierrc @@ -0,0 +1,4 @@ +{ + "trailingComma": "es5", + "singleQuote": true +} \ No newline at end of file diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..868f1ec --- /dev/null +++ b/.travis.yml @@ -0,0 +1,3 @@ +language: node_js +node_js: + - "8.11.2" \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..9de35af --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,48 @@ +# Contributing + +Thanks for (thinking about) contributing to Gekko, all help is wanted! + +## Before Submitting an Issue + +First, please do a search in [open issues](https://github.com/askmike/gekko/issues/) to see if the issue or feature request has already been filed. + +If you find your issue already exists, make relevant comments and add your reaction: + +👍 - upvote + +👎 - downvote + +If you cannot find an existing issue that describes your bug or feature, submit an issue using the guidelines below. + +### Submitting an issue (bug) + +* Be as clear and concise as possible. +* Do not submit more than one bug in a single issue. +* Clearly explain the following: + +1. Action taken (what you did) +2. Expected result (what you hoped would happen) +3. Actual result (unexpected outcome) +4. Your setup (any and all relevant technical facts, error messages, logs, etc) + +### Feature requests + +* First consider if **you** yourself might be able to work on the feature? If so, check out info on contributing code below. +* If you do not have the skills, then please take the time to clearly think through the request. Is is appropriate for all users of Gekko? If so, create a new issue containing the following: + +1. Write `[request]` as the first item in the title, followed by a short, concise description, eg: `[request] Export data to CSV`. +2. Fill in details explaining your request. Be as clear and concise as possible and only 1 request at a time. +3. Add mock-ups, etc as needed to clearly demonstrate the idea. + +## New Feature and code contributions + +- If you want to add an exchange to Gekko, see [this doc](https://gekko.wizb.it/docs/extending/add_an_exchange.html) for all the information you need. +- If you want to Gekko react to anything from the market, you can most likely put this functionality into a plugin. See [this document](https://gekko.wizb.it/docs/internals/plugins.html) for details. +- If you want to create a strategy, please see [this page](https://gekko.wizb.it/docs/strategies/creating_a_strategy.html). +- If you just want to work on Gekko, you can use the open issues with the tag `open-for-pulls` for inspiration. +- If you want to work on the web interface (Gekko UI), please see [this frontend doc](https://gekko.wizb.it/docs/internals/gekko_ui.html) on the Vue.js frontend. + +Things to take into consideration when submitting a pull request: + + - Please submit all pull requests (except for hotfixes) to the [develop branch](https://github.com/askmike/gekko/tree/develop). + - Please keep current code styling in mind. diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..96e48fe --- /dev/null +++ b/Dockerfile @@ -0,0 +1,34 @@ +FROM node:8 + +ENV HOST localhost +ENV PORT 3000 + +# Create app directory +RUN mkdir -p /usr/src/app +WORKDIR /usr/src/app + +# Install GYP dependencies globally, will be used to code build other dependencies +RUN npm install -g --production node-gyp && \ + npm cache clean --force + +# Install Gekko dependencies +COPY package.json . +RUN npm install --production && \ + npm install --production redis@0.10.0 talib@1.0.2 tulind@0.8.7 pg && \ + npm cache clean --force + +# Install Gekko Broker dependencies +WORKDIR exchange +COPY exchange/package.json . +RUN npm install --production && \ + npm cache clean --force +WORKDIR ../ + +# Bundle app source +COPY . /usr/src/app + +EXPOSE 3000 +RUN chmod +x /usr/src/app/docker-entrypoint.sh +ENTRYPOINT ["/usr/src/app/docker-entrypoint.sh"] + +CMD ["--config", "config.js", "--ui"] diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..0ae9896 --- /dev/null +++ b/LICENSE @@ -0,0 +1,9 @@ +The MIT License (MIT) + +Copyright (c) 2014-2017 Mike van Rossum mike@mvr.me + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..86411dd --- /dev/null +++ b/README.md @@ -0,0 +1,43 @@ +# This repo is not maintained anymore + +I am officially not maintaining this project anymore. It was an amazing journey and I want to thank everyone for +playing the role in this amazing story! + +More details can be found here: [https://medium.com/@gekkoplus/archiving-open-source-gekko-dba02e6efc7](https://medium.com/@gekkoplus/archiving-open-source-gekko-dba02e6efc7) + +This only impacts my Gekko repo (askmike/gekko). There might be other forks of Gekko out there that are being maintained! + +------ + +Old content: + +------ + + +# Gekko [![npm](https://img.shields.io/npm/dm/gekko.svg)]() [![Build Status](https://travis-ci.org/askmike/gekko.png)](https://travis-ci.org/askmike/gekko) [![Build status](https://ci.appveyor.com/api/projects/status/github/askmike/gekko?branch=stable&svg=true)](https://ci.appveyor.com/project/askmike/gekko) + +![Gordon Gekko](http://mikevanrossum.nl/static/gekko.jpg) + +*The most valuable commodity I know of is information.* + +-Gordon Gekko + +Gekko is a Bitcoin TA trading and backtesting platform that connects to popular Bitcoin exchanges. It is written in JavaScript and runs on [Node.js](http://nodejs.org). + +*Use Gekko at your own risk.* + +## Documentation + +See [the documentation website](https://gekko.wizb.it/docs/introduction/about_gekko.html). + +## Installation & Usage + +See [the installing Gekko doc](https://gekko.wizb.it/docs/installation/installing_gekko.html). + +## Community & Support + +Gekko has [a forum](https://forum.gekko.wizb.it/) that is the place for discussions on using Gekko, automated trading and exchanges. In case you rather want to chat in realtime about Gekko feel free to join the [Gekko Support Discord](https://discord.gg/26wMygt). + +## Final + +If Gekko helped you in any way, you can always leave me a tip at (BTC) 13r1jyivitShUiv9FJvjLH7Nh1ZZptumwW diff --git a/appveyor.yml b/appveyor.yml new file mode 100644 index 0000000..e9069ae --- /dev/null +++ b/appveyor.yml @@ -0,0 +1,15 @@ +init: + - git config --global core.autocrlf input + +environment: + nodejs_version: "9" + +install: + - ps: Install-Product node $env:nodejs_version + - npm install +test_script: + - node --version + - npm --version + - cmd: npm test + +build: off \ No newline at end of file diff --git a/config/adapters/mongodb.toml b/config/adapters/mongodb.toml new file mode 100644 index 0000000..2aed9d1 --- /dev/null +++ b/config/adapters/mongodb.toml @@ -0,0 +1,7 @@ +connectionString = "mongodb://mongodb/gekko" +path = "plugins/mongodb" +version = 0.1 + +[[dependencies]] +module = "mongojs" +version = "2.4.0" \ No newline at end of file diff --git a/config/adapters/postgresql.toml b/config/adapters/postgresql.toml new file mode 100644 index 0000000..834f442 --- /dev/null +++ b/config/adapters/postgresql.toml @@ -0,0 +1,7 @@ +connectionString = "postgres://user:pass@localhost:5432" +path = "plugins/postgresql" +version = 0.1 + +[[dependencies]] +module = "pg" +version = "6.1.0" \ No newline at end of file diff --git a/config/adapters/sqlite.toml b/config/adapters/sqlite.toml new file mode 100644 index 0000000..48e6833 --- /dev/null +++ b/config/adapters/sqlite.toml @@ -0,0 +1,7 @@ +dataDirectory = "history" +path = "plugins/sqlite" +version = 0.1 + +[[dependencies]] +module = "sqlite3" +version = "3.1.4" \ No newline at end of file diff --git a/config/backtest.toml b/config/backtest.toml new file mode 100644 index 0000000..3569ec1 --- /dev/null +++ b/config/backtest.toml @@ -0,0 +1,8 @@ +batchSize = 50 + +# scan for available dateranges +daterange = "scan" +# or specify it like so: +# [daterange] +# from = "2015-01-01" +# to = "2016-01-01" \ No newline at end of file diff --git a/config/general.toml b/config/general.toml new file mode 100644 index 0000000..6da631f --- /dev/null +++ b/config/general.toml @@ -0,0 +1,10 @@ +debug = true + +# what database should Gekko use? +adapter = 'sqlite' + +[watch] +exchange = 'Poloniex' +currency = 'USDT' +asset = 'BTC' + diff --git a/config/plugins/candleWriter.toml b/config/plugins/candleWriter.toml new file mode 100644 index 0000000..47ee1d5 --- /dev/null +++ b/config/plugins/candleWriter.toml @@ -0,0 +1,2 @@ +enabled = true +adapter = "sqlite" \ No newline at end of file diff --git a/config/plugins/paperTrader.toml b/config/plugins/paperTrader.toml new file mode 100644 index 0000000..06abc1e --- /dev/null +++ b/config/plugins/paperTrader.toml @@ -0,0 +1,8 @@ +feeMaker = 0.25 +feeTaker = 0.25 +feeUsing = 'maker' +slippage = 0.05 + +[simulationBalance] +asset = 1 +currency = 100 diff --git a/config/plugins/performanceAnalyzer.toml b/config/plugins/performanceAnalyzer.toml new file mode 100644 index 0000000..1aa3051 --- /dev/null +++ b/config/plugins/performanceAnalyzer.toml @@ -0,0 +1,5 @@ +# Used to calculate sharpe ratio +# yearly % of riskfree return for example +# treasury bonds or bank interest. + +riskFreeReturn = 2 \ No newline at end of file diff --git a/config/plugins/pushbullet.toml b/config/plugins/pushbullet.toml new file mode 100644 index 0000000..686352e --- /dev/null +++ b/config/plugins/pushbullet.toml @@ -0,0 +1,16 @@ +enabled = false + +# Send 'Gekko starting' message if true +sendMessageOnStart = true + +# disable advice printout if it's soft +muteSoft = true + +# your email, change it unless you are Azor Ahai +email = "jon_snow@westeros.org" + +# your pushbullet API key +key = "xxx" + +# will make Gekko messages start with [GEKKO] +tag = "[GEKKO]" \ No newline at end of file diff --git a/config/plugins/trader.toml b/config/plugins/trader.toml new file mode 100644 index 0000000..8a76fc2 --- /dev/null +++ b/config/plugins/trader.toml @@ -0,0 +1,9 @@ +enabled = false + +## NOTE: once you filled in the following +## never share this file with anyone! + +key = "" +secret = "" +# this is only required at specific exchanges +username = "" \ No newline at end of file diff --git a/config/plugins/tradingAdvisor.toml b/config/plugins/tradingAdvisor.toml new file mode 100644 index 0000000..54bc8e4 --- /dev/null +++ b/config/plugins/tradingAdvisor.toml @@ -0,0 +1,9 @@ +enabled = true + +candleSize = 60 +historySize = 25 +method = "MACD" + +[talib] +enabled = false +version = "1.0.2" \ No newline at end of file diff --git a/config/strategies/CCI.toml b/config/strategies/CCI.toml new file mode 100644 index 0000000..eb21d88 --- /dev/null +++ b/config/strategies/CCI.toml @@ -0,0 +1,10 @@ +# constant multiplier. 0.015 gets to around 70% fit +constant = 0.015 + +# history size, make same or smaller than history +history = 90 + +[thresholds] +up = 100 +down = -100 +persistence = 0 \ No newline at end of file diff --git a/config/strategies/DEMA.toml b/config/strategies/DEMA.toml new file mode 100644 index 0000000..0916c41 --- /dev/null +++ b/config/strategies/DEMA.toml @@ -0,0 +1,5 @@ +weight = 21 + +[thresholds] +down = -0.025 +up = 0.025 diff --git a/config/strategies/MACD.toml b/config/strategies/MACD.toml new file mode 100644 index 0000000..15823f0 --- /dev/null +++ b/config/strategies/MACD.toml @@ -0,0 +1,8 @@ +short = 10 +long = 21 +signal = 9 + +[thresholds] +down = -0.025 +up = 0.025 +persistence = 1 \ No newline at end of file diff --git a/config/strategies/PPO.toml b/config/strategies/PPO.toml new file mode 100644 index 0000000..0492f10 --- /dev/null +++ b/config/strategies/PPO.toml @@ -0,0 +1,8 @@ +short = 12 +long = 26 +signal = 9 + +[thresholds] +down = -0.025 +up = 0.025 +persistence = 2 \ No newline at end of file diff --git a/config/strategies/RSI.toml b/config/strategies/RSI.toml new file mode 100644 index 0000000..ebc39c4 --- /dev/null +++ b/config/strategies/RSI.toml @@ -0,0 +1,6 @@ +interval = 14 + +[thresholds] +low = 30 +high = 70 +persistence = 1 \ No newline at end of file diff --git a/config/strategies/StochRSI.toml b/config/strategies/StochRSI.toml new file mode 100644 index 0000000..b899b8c --- /dev/null +++ b/config/strategies/StochRSI.toml @@ -0,0 +1,6 @@ +interval = 3 + +[thresholds] +low = 20 +high = 80 +persistence = 3 \ No newline at end of file diff --git a/config/strategies/TMA.toml b/config/strategies/TMA.toml new file mode 100644 index 0000000..f915741 --- /dev/null +++ b/config/strategies/TMA.toml @@ -0,0 +1,3 @@ +short = 7 +medium = 25 +long = 99 diff --git a/config/strategies/TSI.toml b/config/strategies/TSI.toml new file mode 100644 index 0000000..1d0cf43 --- /dev/null +++ b/config/strategies/TSI.toml @@ -0,0 +1,7 @@ +short = 13 +long = 25 + +[thresholds] +low = -25 +high = 25 +persistence = 1 \ No newline at end of file diff --git a/config/strategies/UO.toml b/config/strategies/UO.toml new file mode 100644 index 0000000..f8fd3f1 --- /dev/null +++ b/config/strategies/UO.toml @@ -0,0 +1,16 @@ +[first] +weight = 4 +period = 7 + +[second] +weight = 2 +period = 14 + +[third] +weight = 1 +period = 28 + +[thresholds] +low = 30 +high = 70 +persistence = 1 diff --git a/config/strategies/custom.toml b/config/strategies/custom.toml new file mode 100644 index 0000000..7d80674 --- /dev/null +++ b/config/strategies/custom.toml @@ -0,0 +1 @@ +my_custom_setting = 10 \ No newline at end of file diff --git a/config/strategies/talib-macd.toml b/config/strategies/talib-macd.toml new file mode 100644 index 0000000..3bb3ab8 --- /dev/null +++ b/config/strategies/talib-macd.toml @@ -0,0 +1,8 @@ +[parameters] +optInFastPeriod = 10 +optInSlowPeriod = 21 +optInSignalPeriod = 9 + +[thresholds] +down = -0.025 +up = 0.025 \ No newline at end of file diff --git a/config/strategies/tulip-adx.toml b/config/strategies/tulip-adx.toml new file mode 100644 index 0000000..c853957 --- /dev/null +++ b/config/strategies/tulip-adx.toml @@ -0,0 +1,7 @@ +historySize = 80 +optInTimePeriod = 15 +candleSize = 10 + +[thresholds] +up = 30 +down = 20 \ No newline at end of file diff --git a/config/strategies/tulip-macd.toml b/config/strategies/tulip-macd.toml new file mode 100644 index 0000000..3bb3ab8 --- /dev/null +++ b/config/strategies/tulip-macd.toml @@ -0,0 +1,8 @@ +[parameters] +optInFastPeriod = 10 +optInSlowPeriod = 21 +optInSignalPeriod = 9 + +[thresholds] +down = -0.025 +up = 0.025 \ No newline at end of file diff --git a/config/strategies/tulip-multi-strat.toml b/config/strategies/tulip-multi-strat.toml new file mode 100644 index 0000000..3e8b549 --- /dev/null +++ b/config/strategies/tulip-multi-strat.toml @@ -0,0 +1,12 @@ +optInTimePeriod = 2 +optInFastPeriod = 4 +optInSlowPeriod = 7 +optInSignalPeriod = 23.707189677282354 + +candleSize = 1 +historySize = 4 + +up = 24.52 +down = 55.74 +macd_up = 14.498233170768081 +macd_down = -9.65220944122072 diff --git a/config/strategies/varPPO.toml b/config/strategies/varPPO.toml new file mode 100644 index 0000000..43eb4e0 --- /dev/null +++ b/config/strategies/varPPO.toml @@ -0,0 +1,9 @@ +momentum = "TSI" # RSI, TSI or UO + +[thresholds] +weightLow = 120 +weightHigh = -120 + +# How many candle intervals should a trend persist +# before we consider it real? +persistence = 0 \ No newline at end of file diff --git a/core/budfox/budfox.js b/core/budfox/budfox.js new file mode 100644 index 0000000..26d371f --- /dev/null +++ b/core/budfox/budfox.js @@ -0,0 +1,78 @@ +// Budfox is the realtime market for Gekko! +// +// Read more here: +// @link https://github.com/askmike/gekko/blob/stable/docs/internals/budfox.md +// +// > [getting up] I don't know. I guess I realized that I'm just Bud Fox. +// > As much as I wanted to be Gordon Gekko, I'll *always* be Bud Fox. +// > [tosses back the handkerchief and walks away] + +var _ = require('lodash'); +var async = require('async'); + +var util = require(__dirname + '/../util'); +var dirs = util.dirs(); + +var Heart = require(dirs.budfox + 'heart'); +var MarketDataProvider = require(dirs.budfox + 'marketDataProvider'); +var CandleManager = require(dirs.budfox + 'candleManager'); + +var BudFox = function(config) { + _.bindAll(this); + + Readable.call(this, {objectMode: true}); + + // BudFox internal modules: + + this.heart = new Heart; + this.marketDataProvider = new MarketDataProvider(config); + this.candleManager = new CandleManager; + + // BudFox data flow: + + // relay a marketUpdate event + this.marketDataProvider.on( + 'marketUpdate', + e => this.emit('marketUpdate', e) + ); + + // relay a marketStart event + this.marketDataProvider.on( + 'marketStart', + e => this.emit('marketStart', e) + ); + + // Output the candles + this.candleManager.on( + 'candles', + this.pushCandles + ); + + // on every `tick` retrieve trade data + this.heart.on( + 'tick', + this.marketDataProvider.retrieve + ); + + // on new trade data create candles + this.marketDataProvider.on( + 'trades', + this.candleManager.processTrades + ); + + this.heart.pump(); +} + +var Readable = require('stream').Readable; + +BudFox.prototype = Object.create(Readable.prototype, { + constructor: { value: BudFox } +}); + +BudFox.prototype._read = function noop() {} + +BudFox.prototype.pushCandles = function(candles) { + _.each(candles, this.push); +} + +module.exports = BudFox; diff --git a/core/budfox/candleCreator.js b/core/budfox/candleCreator.js new file mode 100644 index 0000000..36c0940 --- /dev/null +++ b/core/budfox/candleCreator.js @@ -0,0 +1,200 @@ +// The CandleCreator creates one minute candles based on trade batches. Note +// that it also adds empty candles to fill gaps with no volume. +// +// Expects trade batches to be written like: +// +// { +// amount: x, +// start: (moment), +// end: (moment), +// first: (trade), +// last: (trade), +// timespan: x, +// all: [ +// // batch of new trades with +// // moments instead of timestamps +// ] +// } +// +// Emits 'new candles' event with: +// +// [ +// { +// start: (moment), +// end: (moment), +// high: (float), +// open: (float), +// low: (float), +// close: (float) +// volume: (float) +// vwp: (float) // volume weighted price +// }, +// { +// start: (moment), // + 1 +// end: (moment), +// high: (float), +// open: (float), +// low: (float), +// close: (float) +// volume: (float) +// vwp: (float) // volume weighted price +// } +// // etc. +// ] +// + +var _ = require('lodash'); +var moment = require('moment'); + +var util = require(__dirname + '/../util'); + +var CandleCreator = function() { + _.bindAll(this); + + // TODO: remove fixed date + this.threshold = moment("1970-01-01", "YYYY-MM-DD"); + + // This also holds the leftover between fetches + this.buckets = {}; +} + +util.makeEventEmitter(CandleCreator); + +CandleCreator.prototype.write = function(batch) { + var trades = batch.data; + + if(_.isEmpty(trades)) + return; + + trades = this.filter(trades); + this.fillBuckets(trades); + var candles = this.calculateCandles(); + + candles = this.addEmptyCandles(candles); + + if(_.isEmpty(candles)) + return; + + // the last candle is not complete + this.threshold = candles.pop().start; + + this.emit('candles', candles); +} + +CandleCreator.prototype.filter = function(trades) { + // make sure we only include trades more recent + // than the previous emitted candle + return _.filter(trades, function(trade) { + return trade.date > this.threshold; + }, this); +} + +// put each trade in a per minute bucket +CandleCreator.prototype.fillBuckets = function(trades) { + _.each(trades, function(trade) { + var minute = trade.date.format('YYYY-MM-DD HH:mm'); + + if(!(minute in this.buckets)) + this.buckets[minute] = []; + + this.buckets[minute].push(trade); + }, this); + + this.lastTrade = _.last(trades); +} + +// convert each bucket into a candle +CandleCreator.prototype.calculateCandles = function() { + var minutes = _.size(this.buckets); + + // catch error from high volume getTrades + if (this.lastTrade !== undefined) + // create a string referencing the minute this trade happened in + var lastMinute = this.lastTrade.date.format('YYYY-MM-DD HH:mm'); + + var candles = _.map(this.buckets, function(bucket, name) { + var candle = this.calculateCandle(bucket); + + // clean all buckets, except the last one: + // this candle is not complete + if(name !== lastMinute) + delete this.buckets[name]; + + return candle; + }, this); + + return candles; +} + +CandleCreator.prototype.calculateCandle = function(trades) { + var first = _.first(trades); + + var f = parseFloat; + + var candle = { + start: first.date.clone().startOf('minute'), + open: f(first.price), + high: f(first.price), + low: f(first.price), + close: f(_.last(trades).price), + vwp: 0, + volume: 0, + trades: _.size(trades) + }; + + _.each(trades, function(trade) { + candle.high = _.max([candle.high, f(trade.price)]); + candle.low = _.min([candle.low, f(trade.price)]); + candle.volume += f(trade.amount); + candle.vwp += f(trade.price) * f(trade.amount); + }); + + candle.vwp /= candle.volume; + + return candle; +} + +// Gekko expects a candle every minute, if nothing happened +// during a particilar minute Gekko will add empty candles with: +// +// - open, high, close, low, vwp are the same as the close of the previous candle. +// - trades, volume are 0 +CandleCreator.prototype.addEmptyCandles = function(candles) { + var amount = _.size(candles); + if(!amount) + return candles; + + // iterator + var start = _.first(candles).start.clone(); + var end = _.last(candles).start; + var i, j = -1; + + var minutes = _.map(candles, function(candle) { + return +candle.start; + }); + + while(start < end) { + start.add(1, 'm'); + i = +start; + j++; + + if(_.contains(minutes, i)) + continue; // we have a candle for this minute + + var lastPrice = candles[j].close; + + candles.splice(j + 1, 0, { + start: start.clone(), + open: lastPrice, + high: lastPrice, + low: lastPrice, + close: lastPrice, + vwp: lastPrice, + volume: 0, + trades: 0 + }); + } + return candles; +} + +module.exports = CandleCreator; diff --git a/core/budfox/candleManager.js b/core/budfox/candleManager.js new file mode 100644 index 0000000..b90cc69 --- /dev/null +++ b/core/budfox/candleManager.js @@ -0,0 +1,34 @@ +// The candleManager consumes trades and emits: +// - `candles`: array of minutly candles. +// - `candle`: the most recent candle after a fetch Gekko. + +var _ = require('lodash'); +var moment = require('moment'); +var fs = require('fs'); + +var util = require(__dirname + '/../util'); +var dirs = util.dirs(); +var config = util.getConfig(); +var log = require(dirs.core + 'log'); + +var CandleCreator = require(dirs.budfox + 'candleCreator'); + +var Manager = function() { + _.bindAll(this); + + this.candleCreator = new CandleCreator; + + this.candleCreator + .on('candles', this.relayCandles); +}; + +util.makeEventEmitter(Manager); +Manager.prototype.processTrades = function(tradeBatch) { + this.candleCreator.write(tradeBatch); +} + +Manager.prototype.relayCandles = function(candles) { + this.emit('candles', candles); +} + +module.exports = Manager; diff --git a/core/budfox/heart.js b/core/budfox/heart.js new file mode 100644 index 0000000..82f92f5 --- /dev/null +++ b/core/budfox/heart.js @@ -0,0 +1,51 @@ +// The heart schedules and emit ticks every 20 seconds. + +var util = require(__dirname + '/../util'); +var log = require(util.dirs().core + 'log'); + +var _ = require('lodash'); +var moment = require('moment'); + +if (util.getConfig().watch.tickrate) + var TICKRATE = util.getConfig().watch.tickrate; +else if(util.getConfig().watch.exchange === 'okcoin') + var TICKRATE = 2; +else + var TICKRATE = 20; + +var Heart = function() { + this.lastTick = false; + + _.bindAll(this); +} + +util.makeEventEmitter(Heart); + +Heart.prototype.pump = function() { + log.debug('scheduling ticks'); + this.scheduleTicks(); +} + +Heart.prototype.tick = function() { + if(this.lastTick) { + // make sure the last tick happened not to lang ago + // @link https://github.com/askmike/gekko/issues/514 + if(this.lastTick < moment().unix() - TICKRATE * 3) + util.die('Failed to tick in time, see https://github.com/askmike/gekko/issues/514 for details', true); + } + + this.lastTick = moment().unix(); + this.emit('tick'); +} + +Heart.prototype.scheduleTicks = function() { + setInterval( + this.tick, + +moment.duration(TICKRATE, 's') + ); + + // start! + _.defer(this.tick); +} + +module.exports = Heart; diff --git a/core/budfox/marketDataProvider.js b/core/budfox/marketDataProvider.js new file mode 100644 index 0000000..6576f20 --- /dev/null +++ b/core/budfox/marketDataProvider.js @@ -0,0 +1,45 @@ +// +// The market data provider will fetch data from a datasource on tick. It emits: +// +// - `trades`: batch of newly detected trades +// - `trade`: after Gekko fetched new trades, this +// will be the most recent one. + +const _ = require('lodash'); +const util = require(__dirname + '/../util'); + +const MarketFetcher = require('./marketFetcher'); +const dirs = util.dirs(); + +const Manager = function(config) { + + _.bindAll(this); + + // fetch trades + this.source = new MarketFetcher(config); + + // relay newly fetched trades + this.source + .on('trades batch', this.relayTrades); +} + +util.makeEventEmitter(Manager); + +// HANDLERS +Manager.prototype.retrieve = function() { + this.source.fetch(); +} + + +Manager.prototype.relayTrades = function(batch) { + this.sendMarketStart(batch); + this.emit('marketUpdate', batch.last.date); + + this.emit('trades', batch); +} + +Manager.prototype.sendMarketStart = _.once(function(batch) { + this.emit('marketStart', batch.first.date); +}); + +module.exports = Manager; \ No newline at end of file diff --git a/core/budfox/marketFetcher.js b/core/budfox/marketFetcher.js new file mode 100644 index 0000000..2e6c657 --- /dev/null +++ b/core/budfox/marketFetcher.js @@ -0,0 +1,109 @@ +// +// The fetcher is responsible for fetching new +// market data at the exchange on interval. It will emit +// the following events: +// +// - `trades batch` - all new trades. +// - `trade` - the most recent trade after every fetch + +const _ = require('lodash'); +const moment = require('moment'); +const utc = moment.utc; +const util = require(__dirname + '/../util'); +const dirs = util.dirs(); + +const config = util.getConfig(); +const log = require(dirs.core + 'log'); +const exchangeChecker = require(dirs.gekko + 'exchange/exchangeChecker'); + +const TradeBatcher = require(util.dirs().budfox + 'tradeBatcher'); + +const Fetcher = function(config) { + if(!_.isObject(config)) + throw new Error('TradeFetcher expects a config'); + + const exchangeName = config.watch.exchange.toLowerCase(); + const DataProvider = require(util.dirs().gekko + 'exchange/wrappers/' + exchangeName); + _.bindAll(this); + + // Create a public dataProvider object which can retrieve live + // trade information from an exchange. + this.exchangeTrader = new DataProvider(config.watch); + + this.exchange = exchangeChecker.settings(config.watch); + + var requiredHistory = config.tradingAdvisor.candleSize * config.tradingAdvisor.historySize; + + // If the trading adviser is enabled we might need a very specific fetch since + // to line up [local db, trading method, and fetching] + if(config.tradingAdvisor.enabled && config.tradingAdvisor.firstFetchSince) { + this.firstSince = config.tradingAdvisor.firstFetchSince; + + if(this.exchange.providesHistory === 'date') { + this.firstSince = moment.unix(this.firstSince).utc(); + } + } + + this.batcher = new TradeBatcher(this.exchange.tid); + + this.pair = [ + config.watch.asset, + config.watch.currency + ].join('/'); + + log.info('Starting to watch the market:', + this.exchange.name, + this.pair + ); + + // if the exchange returns an error + // we will keep on retrying until next + // scheduled fetch. + this.tries = 0; + this.limit = 20; // [TODO] + + this.firstFetch = true; + + this.batcher.on('new batch', this.relayTrades); +} + +util.makeEventEmitter(Fetcher); + +Fetcher.prototype._fetch = function(since) { + if(++this.tries >= this.limit) + return; + + this.exchangeTrader.getTrades(since, this.processTrades, false); +} + +Fetcher.prototype.fetch = function() { + var since = false; + if(this.firstFetch) { + since = this.firstSince; + this.firstFetch = false; + } else + since = false; + + this.tries = 0; + log.debug('Requested', this.pair, 'trade data from', this.exchange.name, '...'); + this._fetch(since); +} + +Fetcher.prototype.processTrades = function(err, trades) { + if(err || _.isEmpty(trades)) { + if(err) { + log.warn(this.exchange.name, 'returned an error while fetching trades:', err); + log.debug('refetching...'); + } else + log.debug('Trade fetch came back empty, refetching...'); + setTimeout(this._fetch, +moment.duration('s', 1)); + return; + } + this.batcher.write(trades); +} + +Fetcher.prototype.relayTrades = function(batch) { + this.emit('trades batch', batch); +} + +module.exports = Fetcher; diff --git a/core/budfox/tradeBatcher.js b/core/budfox/tradeBatcher.js new file mode 100644 index 0000000..20bdb16 --- /dev/null +++ b/core/budfox/tradeBatcher.js @@ -0,0 +1,124 @@ +// +// Small wrapper that only propagates new trades. +// +// Expects trade batches to be written like: +// [ +// { +// tid: x, // tid is preferred, but if none available date will also work +// price: x, +// date: (timestamp), +// amount: x +// }, +// { +// tid: x + 1, +// price: x, +// date: (timestamp), +// amount: x +// } +// ] +// +// Emits 'new trades' event with: +// { +// amount: x, +// start: (moment), +// end: (moment), +// first: (trade), +// last: (trade) +// data: [ +// // batch of new trades with +// // moments instead of timestamps +// ] +// } + +var _ = require('lodash'); +var moment = require('moment'); +var util = require('../util'); +var log = require('../log'); + +var TradeBatcher = function(tid) { + if(!_.isString(tid)) + throw new Error('tid is not a string'); + + _.bindAll(this); + this.tid = tid; + this.last = -1; +} + +util.makeEventEmitter(TradeBatcher); + +TradeBatcher.prototype.write = function(batch) { + + if(!_.isArray(batch)) + throw new Error('batch is not an array'); + + if(_.isEmpty(batch)) + return log.debug('Trade fetch came back empty.'); + + var filterBatch = this.filter(batch); + + var amount = _.size(filterBatch); + if(!amount) + return log.debug('No new trades.'); + + var momentBatch = this.convertDates(filterBatch); + + var last = _.last(momentBatch); + var first = _.first(momentBatch); + + log.debug( + 'Processing', amount, 'new trades.', + 'From', + first.date.format('YYYY-MM-DD HH:mm:ss'), + 'UTC to', + last.date.format('YYYY-MM-DD HH:mm:ss'), + 'UTC.', + '(' + first.date.from(last.date, true) + ')' + ); + + this.emit('new batch', { + amount: amount, + start: first.date, + end: last.date, + last: last, + first: first, + data: momentBatch + }); + + this.last = last[this.tid]; + + // we overwrote those, get unix ts back + if(this.tid === 'date') + this.last = this.last.unix(); + +} + +TradeBatcher.prototype.filter = function(batch) { + // make sure we're not trying to count + // beyond infinity + var lastTid = _.last(batch)[this.tid]; + if(lastTid === lastTid + 1) + util.die('trade tid is max int, Gekko can\'t process..'); + + // remove trades that have zero amount + // see @link + // https://github.com/askmike/gekko/issues/486 + batch = _.filter(batch, function(trade) { + return trade.amount > 0; + }); + + // weed out known trades + // TODO: optimize by stopping as soon as the + // first trade is too old (reverse first) + return _.filter(batch, function(trade) { + return this.last < trade[this.tid]; + }, this); +} + +TradeBatcher.prototype.convertDates = function(batch) { + return _.map(_.cloneDeep(batch), function(trade) { + trade.date = moment.unix(trade.date).utc(); + return trade; + }); +} + +module.exports = TradeBatcher; diff --git a/core/candleBatcher.js b/core/candleBatcher.js new file mode 100644 index 0000000..0cb5ade --- /dev/null +++ b/core/candleBatcher.js @@ -0,0 +1,93 @@ +// internally we only use 1m +// candles, this can easily +// convert them to any desired +// size. + +// Acts as ~fake~ stream: takes +// 1m candles as input and emits +// bigger candles. +// +// input are transported candles. + +var _ = require('lodash'); +var util = require(__dirname + '/util'); + +var CandleBatcher = function(candleSize) { + if(!_.isNumber(candleSize)) + throw new Error('candleSize is not a number'); + + this.candleSize = candleSize; + this.smallCandles = []; + this.calculatedCandles = []; + + _.bindAll(this); +} + +util.makeEventEmitter(CandleBatcher); + +CandleBatcher.prototype.write = function(candles) { + if(!_.isArray(candles)) { + throw new Error('candles is not an array'); + } + + this.emitted = 0; + + _.each(candles, function(candle) { + this.smallCandles.push(candle); + this.check(); + }, this); + + return this.emitted; +} + +CandleBatcher.prototype.check = function() { + if(_.size(this.smallCandles) % this.candleSize !== 0) + return; + + this.emitted++; + this.calculatedCandles.push(this.calculate()); + this.smallCandles = []; +} + +CandleBatcher.prototype.flush = function() { + _.each( + this.calculatedCandles, + candle => this.emit('candle', candle) + ); + + this.calculatedCandles = []; +} + +CandleBatcher.prototype.calculate = function() { + // remove the id property of the small candle + var { id, ...first } = this.smallCandles.shift(); + + first.vwp = first.vwp * first.volume; + + var candle = _.reduce( + this.smallCandles, + function(candle, m) { + candle.high = _.max([candle.high, m.high]); + candle.low = _.min([candle.low, m.low]); + candle.close = m.close; + candle.volume += m.volume; + candle.vwp += m.vwp * m.volume; + candle.trades += m.trades; + return candle; + }, + first + ); + + if(candle.volume) + // we have added up all prices (relative to volume) + // now divide by volume to get the Volume Weighted Price + candle.vwp /= candle.volume; + else + // empty candle + candle.vwp = candle.open; + + candle.start = first.start; + return candle; +} + +module.exports = CandleBatcher; diff --git a/core/emitter.js b/core/emitter.js new file mode 100644 index 0000000..409c5d9 --- /dev/null +++ b/core/emitter.js @@ -0,0 +1,34 @@ +// Gekko uses a custom event emitter within the GekkoStream (the plugins) to guarantee +// the correct order of events that are triggered by eachother. Turns sync events from +// LIFO into a FIFO stack based model. +// +// More details here: https://forum.gekko.wizb.it/thread-56579.html + +const util = require('util'); +const events = require('events'); +const NativeEventEmitter = events.EventEmitter; + +const GekkoEventEmitter = function() { + NativeEventEmitter.call(this); + this.defferedEvents = []; +} + +util.inherits(GekkoEventEmitter, NativeEventEmitter); + +// push to stack +GekkoEventEmitter.prototype.deferredEmit = function(name, payload) { + this.defferedEvents.push({name, payload}); +} + +// resolve FIFO +GekkoEventEmitter.prototype.broadcastDeferredEmit = function() { + if(this.defferedEvents.length === 0) + return false; + + const event = this.defferedEvents.shift(); + + this.emit(event.name, event.payload); + return true; +} + +module.exports = GekkoEventEmitter; \ No newline at end of file diff --git a/core/gekkoStream.js b/core/gekkoStream.js new file mode 100644 index 0000000..6171398 --- /dev/null +++ b/core/gekkoStream.js @@ -0,0 +1,121 @@ +// Small writable stream wrapper that +// passes data to all `candleConsumers`. + +const Writable = require('stream').Writable; +const _ = require('lodash'); +const async = require('async'); +const moment = require('moment'); + +const util = require('./util'); +const env = util.gekkoEnv(); +const mode = util.gekkoMode(); +const config = util.getConfig(); +const log = require(util.dirs().core + 'log'); + +var Gekko = function(plugins) { + this.plugins = plugins; + this.candleConsumers = plugins + .filter(plugin => plugin.processCandle); + Writable.call(this, {objectMode: true}); + + this.producers = this.plugins + .filter(p => p.meta.emits); + + this.finalize = _.bind(this.finalize, this); +} + +Gekko.prototype = Object.create(Writable.prototype, { + constructor: { value: Gekko } +}); + +if(config.debug && mode !== 'importer') { + // decorate with more debug information + Gekko.prototype._write = function(chunk, encoding, _done) { + + if(chunk.isFinished) { + return this.finalize(); + } + + const start = moment(); + var relayed = false; + var at = null; + + const timer = setTimeout(() => { + if(!relayed) + log.error([ + `The plugin "${at}" has not processed a candle for 1 second.`, + `This will cause Gekko to slow down or stop working completely.` + ].join(' ')); + }, 1000); + + const flushEvents = _.after(this.candleConsumers.length, () => { + relayed = true; + clearInterval(timer); + this.flushDefferedEvents(); + _done(); + }); + _.each(this.candleConsumers, function(c) { + at = c.meta.name; + c.processCandle(chunk, flushEvents); + }, this); + } +} else { + // skip decoration + Gekko.prototype._write = function(chunk, encoding, _done) { + if(chunk.isFinished) { + return this.finalize(); + } + + const flushEvents = _.after(this.candleConsumers.length, () => { + this.flushDefferedEvents(); + _done(); + }); + _.each(this.candleConsumers, function(c) { + c.processCandle(chunk, flushEvents); + }, this); + } +} + +Gekko.prototype.flushDefferedEvents = function() { + const broadcasted = _.find( + this.producers, + producer => producer.broadcastDeferredEmit() + ); + + // If we braodcasted anything we might have + // triggered more events, recurse until we + // have fully broadcasted everything. + if(broadcasted) + this.flushDefferedEvents(); +} + +Gekko.prototype.finalize = function() { + var tradingMethod = _.find( + this.candleConsumers, + c => c.meta.name === 'Trading Advisor' + ); + + if(!tradingMethod) + return this.shutdown(); + + tradingMethod.finish(this.shutdown.bind(this)); +} + +Gekko.prototype.shutdown = function() { + this.end(); + async.eachSeries( + this.plugins, + function(c, callback) { + if (c.finalize) c.finalize(callback); + else callback(); + }, + () => { + // If we are a child process, we signal to the parent to kill the child once it is done + // so that is has time to process all remaining events (and send report data) + if (env === 'child-process') process.send('done'); + else process.exit(0); + } + ); +}; + +module.exports = Gekko; \ No newline at end of file diff --git a/core/log.js b/core/log.js new file mode 100644 index 0000000..782141a --- /dev/null +++ b/core/log.js @@ -0,0 +1,81 @@ +/* + + Lightweight logger, print everything that is send to error, warn + and messages to stdout (the terminal). If config.debug is set in config + also print out everything send to debug. + +*/ + +var moment = require('moment'); +var fmt = require('util').format; +var _ = require('lodash'); +var util = require('./util'); +var config = util.getConfig(); +var debug = config.debug; +var silent = config.silent; + +var sendToParent = function() { + var send = method => (...args) => { + process.send({log: method, message: args.join(' ')}); + } + + return { + error: send('error'), + warn: send('warn'), + info: send('info'), + write: send('write') + } +} + +var Log = function() { + _.bindAll(this); + this.env = util.gekkoEnv(); + + if(this.env === 'standalone') + this.output = console; + else if(this.env === 'child-process') + this.output = sendToParent(); +}; + +Log.prototype = { + _write: function(method, args, name) { + if(!name) + name = method.toUpperCase(); + + var message = moment().format('YYYY-MM-DD HH:mm:ss'); + message += ' (' + name + '):\t'; + message += fmt.apply(null, args); + + this.output[method](message); + }, + error: function() { + this._write('error', arguments); + }, + warn: function() { + this._write('warn', arguments); + }, + info: function() { + this._write('info', arguments); + }, + write: function() { + var args = _.toArray(arguments); + var message = fmt.apply(null, args); + this.output.info(message); + } +} + +if(debug) + Log.prototype.debug = function() { + this._write('info', arguments, 'DEBUG'); + } +else + Log.prototype.debug = _.noop; + +if(silent) { + Log.prototype.debug = _.noop; + Log.prototype.info = _.noop; + Log.prototype.warn = _.noop; + Log.prototype.write = _.noop; +} + +module.exports = new Log; \ No newline at end of file diff --git a/core/markets/backtest.js b/core/markets/backtest.js new file mode 100644 index 0000000..95f7b3e --- /dev/null +++ b/core/markets/backtest.js @@ -0,0 +1,108 @@ +var _ = require('lodash'); +var util = require('../util'); +var config = util.getConfig(); +var dirs = util.dirs(); +var log = require(dirs.core + 'log'); +var moment = require('moment'); + +var adapter = config[config.adapter]; +var Reader = require(dirs.gekko + adapter.path + '/reader'); +var daterange = config.backtest.daterange; + +var to = moment.utc(daterange.to); +var from = moment.utc(daterange.from); + +if(to <= from) + util.die('This daterange does not make sense.') + +if(!from.isValid()) + util.die('invalid `from`'); + +if(!to.isValid()) + util.die('invalid `to`'); + +var Market = function() { + + _.bindAll(this); + this.pushing = false; + this.ended = false; + this.closed = false; + + Readable.call(this, {objectMode: true}); + + log.write(''); + log.info('\tWARNING: BACKTESTING FEATURE NEEDS PROPER TESTING'); + log.info('\tWARNING: ACT ON THESE NUMBERS AT YOUR OWN RISK!'); + log.write(''); + + this.reader = new Reader(); + this.batchSize = config.backtest.batchSize; + this.iterator = { + from: from.clone(), + to: from.clone().add(this.batchSize, 'm').subtract(1, 's') + } +} + +var Readable = require('stream').Readable; +Market.prototype = Object.create(Readable.prototype, { + constructor: { value: Market } +}); + +Market.prototype._read = _.once(function() { + this.get(); +}); + +Market.prototype.get = function() { + if(this.iterator.to >= to) { + this.iterator.to = to; + this.ended = true; + } + + this.reader.get( + this.iterator.from.unix(), + this.iterator.to.unix(), + 'full', + this.processCandles + ) +} + +Market.prototype.processCandles = function(err, candles) { + this.pushing = true; + var amount = _.size(candles); + + if(amount === 0) { + if(this.ended) { + this.closed = true; + this.reader.close(); + this.push({isFinished: true}); + } else { + util.die('Query returned no candles (do you have local data for the specified range?)'); + } + } + + if(!this.ended && amount < this.batchSize) { + var d = function(ts) { + return moment.unix(ts).utc().format('YYYY-MM-DD HH:mm:ss'); + } + var from = d(_.first(candles).start); + var to = d(_.last(candles).start); + log.warn(`Simulation based on incomplete market data (${this.batchSize - amount} missing between ${from} and ${to}).`); + } + + _.each(candles, function(c, i) { + c.start = moment.unix(c.start); + this.push(c); + }, this); + + this.pushing = false; + + this.iterator = { + from: this.iterator.from.clone().add(this.batchSize, 'm'), + to: this.iterator.from.clone().add(this.batchSize * 2, 'm').subtract(1, 's') + } + + if(!this.closed) + this.get(); +} + +module.exports = Market; diff --git a/core/markets/importer.js b/core/markets/importer.js new file mode 100644 index 0000000..e46bd3f --- /dev/null +++ b/core/markets/importer.js @@ -0,0 +1,117 @@ +var _ = require('lodash'); +var util = require('../util'); +var config = util.getConfig(); +var dirs = util.dirs(); +var log = require(dirs.core + 'log'); +var moment = require('moment'); +var gekkoEnv = util.gekkoEnv(); + +var adapter = config[config.adapter]; +var daterange = config.importer.daterange; + +var from = moment.utc(daterange.from); + +if(daterange.to) { + var to = moment.utc(daterange.to); +} else { + var to = moment().utc(); + log.debug( + 'No end date specified for importing, setting to', + to.format() + ); +} +log.debug(to.format()); + +if(!from.isValid()) + util.die('invalid `from`'); + +if(!to.isValid()) + util.die('invalid `to`'); + +var TradeBatcher = require(dirs.budfox + 'tradeBatcher'); +var CandleManager = require(dirs.budfox + 'candleManager'); +var exchangeChecker = require(dirs.gekko + 'exchange/exchangeChecker'); + +var error = exchangeChecker.cantFetchFullHistory(config.watch); +if(error) + util.die(error, true); + +var fetcher = require(dirs.importers + config.watch.exchange); + +if(to <= from) + util.die('This daterange does not make sense.') + +var Market = function() { + _.bindAll(this); + this.exchangeSettings = exchangeChecker.settings(config.watch); + + this.tradeBatcher = new TradeBatcher(this.exchangeSettings.tid); + this.candleManager = new CandleManager; + this.fetcher = fetcher({ + to: to, + from: from + }); + + this.done = false; + + this.fetcher.bus.on( + 'trades', + this.processTrades + ); + + this.fetcher.bus.on( + 'done', + function() { + this.done = true; + }.bind(this) + ) + + this.tradeBatcher.on( + 'new batch', + this.candleManager.processTrades + ); + + this.candleManager.on( + 'candles', + this.pushCandles + ); + + Readable.call(this, {objectMode: true}); + + this.get(); +} + +var Readable = require('stream').Readable; +Market.prototype = Object.create(Readable.prototype, { + constructor: { value: Market } +}); + +Market.prototype._read = _.noop; + +Market.prototype.pushCandles = function(candles) { + _.each(candles, this.push); +} + +Market.prototype.get = function() { + this.fetcher.fetch(); +} + +Market.prototype.processTrades = function(trades) { + this.tradeBatcher.write(trades); + + if(this.done) { + log.info('Done importing!'); + this.emit('end'); + return; + } + + if(_.size(trades) && gekkoEnv === 'child-process') { + let lastAtTS = _.last(trades).date; + let lastAt = moment.unix(lastAtTS).utc().format(); + process.send({event: 'marketUpdate', payload: lastAt}); + } + + setTimeout(this.get, 1000); +} + +module.exports = Market; diff --git a/core/markets/leech.js b/core/markets/leech.js new file mode 100644 index 0000000..5c0499f --- /dev/null +++ b/core/markets/leech.js @@ -0,0 +1,91 @@ +// a leech market is "semi-realtime" and pulls out candles of a +// database (which is expected to be updated regularly, like with a +// realtime market running in parallel). + +const _ = require('lodash'); +const moment = require('moment'); + +const util = require('../util'); +const dirs = util.dirs(); +const config = util.getConfig(); + +const exchangeChecker = require(dirs.gekko + 'exchange/exchangeChecker'); + +const adapter = config[config.adapter]; +const Reader = require(dirs.gekko + adapter.path + '/reader'); + +const TICKINTERVAL = 20 * 1000; // 20 seconds + +const slug = config.watch.exchange.toLowerCase(); +const exchange = exchangeChecker.getExchangeCapabilities(slug); + +if(!exchange) + util.die(`Unsupported exchange: ${slug}`) + +const error = exchangeChecker.cantMonitor(config.watch); +if(error) + util.die(error, true); + +if(config.market.from) + var fromTs = moment.utc(config.market.from).unix(); +else + var fromTs = moment().startOf('minute').unix(); + + +var Market = function() { + + _.bindAll(this); + + Readable.call(this, {objectMode: true}); + + this.reader = new Reader(); + this.latestTs = fromTs; + + setInterval( + this.get, + TICKINTERVAL + ); +} + +var Readable = require('stream').Readable; +Market.prototype = Object.create(Readable.prototype, { + constructor: { value: Market } +}); + +Market.prototype._read = _.once(function() { + this.get(); +}); + +Market.prototype.get = function() { + var future = moment().add(1, 'minute').unix(); + + this.reader.get( + this.latestTs, + future, + 'full', + this.processCandles + ) +} + +Market.prototype.processCandles = function(err, candles) { + var amount = _.size(candles); + if(amount === 0) { + // no new candles! + return; + } + + // TODO: + // verify that the correct amount of candles was passed: + // + // if `this.latestTs` was at 10:00 and we receive 3 candles with the latest at 11:00 + // we know we are missing 57 candles... + + _.each(candles, function(c, i) { + c.start = moment.unix(c.start).utc(); + this.push(c); + }, this); + + this.latestTs = _.last(candles).start.unix() + 1; +} + +module.exports = Market; diff --git a/core/markets/realtime.js b/core/markets/realtime.js new file mode 100644 index 0000000..2c39505 --- /dev/null +++ b/core/markets/realtime.js @@ -0,0 +1,19 @@ +const _ = require('lodash'); + +const util = require('../util'); +const dirs = util.dirs(); + +const exchangeChecker = require(dirs.gekko + 'exchange/exchangeChecker'); +const config = util.getConfig(); + +const slug = config.watch.exchange.toLowerCase(); +const exchange = exchangeChecker.getExchangeCapabilities(slug); + +if(!exchange) + util.die(`Unsupported exchange: ${slug}`) + +const error = exchangeChecker.cantMonitor(config.watch); +if(error) + util.die(error, true); + +module.exports = require(dirs.budfox + 'budfox'); \ No newline at end of file diff --git a/core/pipeline.js b/core/pipeline.js new file mode 100644 index 0000000..c31a3a9 --- /dev/null +++ b/core/pipeline.js @@ -0,0 +1,252 @@ +/* + + A pipeline implements a full Gekko Flow based on a config and + a mode. The mode is an abstraction that tells Gekko what market + to load (realtime, backtesting or importing) while making sure + all enabled plugins are actually supported by that market. + + Read more here: + https://gekko.wizb.it/docs/internals/architecture.html + +*/ + + +var util = require('./util'); +var dirs = util.dirs(); + +var _ = require('lodash'); +var async = require('async'); + +var log = require(dirs.core + 'log'); + +var pipeline = (settings) => { + + var mode = settings.mode; + var config = settings.config; + + // prepare a GekkoStream + var GekkoStream = require(dirs.core + 'gekkoStream'); + + // all plugins + var plugins = []; + // all emitting plugins + var emitters = {}; + // all plugins interested in candles + var candleConsumers = []; + + // utility to check and load plugins. + var pluginHelper = require(dirs.core + 'pluginUtil'); + + // meta information about every plugin that tells Gekko + // something about every available plugin + var pluginParameters = require(dirs.gekko + 'plugins'); + // meta information about the events plugins can broadcast + // and how they should hooked up to consumers. + var subscriptions = require(dirs.gekko + 'subscriptions'); + + var market; + + // Instantiate each enabled plugin + var loadPlugins = function(next) { + // load all plugins + async.mapSeries( + pluginParameters, + pluginHelper.load, + function(error, _plugins) { + if(error) + return util.die(error, true); + + plugins = _.compact(_plugins); + next(); + } + ); + }; + + // Some plugins emit their own events, store + // a reference to those plugins. + var referenceEmitters = function(next) { + + _.each(plugins, function(plugin) { + if(plugin.meta.emits) + emitters[plugin.meta.slug] = plugin; + }); + + next(); + } + + // Subscribe all plugins to other emitting plugins + var subscribePlugins = function(next) { + // events broadcasted by plugins + var pluginSubscriptions = _.filter( + subscriptions, + sub => sub.emitter !== 'market' + ); + + // some events can be broadcasted by different + // plugins, however the pipeline only allows a single + // emitting plugin for each event to be enabled. + _.each( + pluginSubscriptions.filter(s => _.isArray(s.emitter)), + subscription => { + // cache full list + subscription.emitters = subscription.emitter; + var singleEventEmitters = subscription.emitter + .filter( + s => _.size(plugins.filter(p => p.meta.slug === s)) + ); + + if(_.size(singleEventEmitters) > 1) { + var error = `Multiple plugins are broadcasting`; + error += ` the event "${subscription.event}" (${singleEventEmitters.join(',')}).`; + error += 'This is unsupported.' + util.die(error); + } else { + subscription.emitter = _.first(singleEventEmitters); + } + } + ); + + // subscribe interested plugins to + // emitting plugins + _.each(plugins, function(plugin) { + _.each(pluginSubscriptions, function(sub) { + + if(plugin[sub.handler]) { + // if a plugin wants to listen + // to something disabled + if(!emitters[sub.emitter]) { + if(!plugin.meta.greedy) { + + let emitterMessage; + if(sub.emitters) { + emitterMessage = 'all of the emitting plugins [ '; + emitterMessage += sub.emitters.join(', '); + emitterMessage += ' ] are disabled.'; + } else { + emitterMessage += 'the emitting plugin (' + sub.emitter; + emitterMessage += ')is disabled.' + } + + log.error([ + plugin.meta.name, + 'wanted to listen to event', + sub.event + ',', + 'however', + emitterMessage, + plugin.meta.name + ' might malfunction because of it.' + ].join(' ')); + } + return; + } + + // attach handler + emitters[sub.emitter] + .on(sub.event, + plugin[ + sub.handler + ]) + } + + }); + }); + + // events broadcasted by the market + var marketSubscriptions = _.filter( + subscriptions, + {emitter: 'market'} + ); + + // subscribe plugins to the market + _.each(plugins, function(plugin) { + _.each(marketSubscriptions, function(sub) { + + if(plugin[sub.handler]) { + if(sub.event === 'candle') + candleConsumers.push(plugin); + } + + }); + }); + + next(); + } + + var prepareMarket = function(next) { + if(mode === 'backtest' && config.backtest.daterange === 'scan') + require(dirs.core + 'prepareDateRange')(next); + else + next(); + } + + var setupMarket = function(next) { + // load a market based on the config (or fallback to mode) + let marketType; + if(config.market) + marketType = config.market.type; + else + marketType = mode; + + var Market = require(dirs.markets + marketType); + + market = new Market(config); + + next(); + } + + var subscribePluginsToMarket = function(next) { + + // events broadcasted by the market + var marketSubscriptions = _.filter( + subscriptions, + {emitter: 'market'} + ); + + _.each(plugins, function(plugin) { + _.each(marketSubscriptions, function(sub) { + + if(sub.event === 'candle') + // these are handled via the market stream + return; + + if(plugin[sub.handler]) { + market.on(sub.event, plugin[sub.handler]); + } + + }); + }); + + next(); + + } + + log.info('Setting up Gekko in', mode, 'mode'); + log.info(''); + + async.series( + [ + loadPlugins, + referenceEmitters, + subscribePlugins, + prepareMarket, + setupMarket, + subscribePluginsToMarket + ], + function() { + + var gekkoStream = new GekkoStream(plugins); + + market + .pipe(gekkoStream) + + // convert JS objects to JSON string + // .pipe(new require('stringify-stream')()) + // output to standard out + // .pipe(process.stdout); + + market.on('end', gekkoStream.finalize); + } + ); + +} + +module.exports = pipeline; \ No newline at end of file diff --git a/core/pluginUtil.js b/core/pluginUtil.js new file mode 100644 index 0000000..1c28c97 --- /dev/null +++ b/core/pluginUtil.js @@ -0,0 +1,120 @@ +var _ = require('lodash'); +var async = require('async'); +var Emitter = require('./emitter'); + +var util = require(__dirname + '/util'); + +var log = require(util.dirs().core + 'log'); + +var config = util.getConfig(); +var pluginDir = util.dirs().plugins; +var gekkoMode = util.gekkoMode(); +var inherits = require('util').inherits; + +var pluginHelper = { + // Checks whether we can load a module + + // @param Object plugin + // plugin config object + // @return String + // error message if we can't + // use the module. + cannotLoad: function(plugin) { + + // verify plugin dependencies are installed + if(_.has(plugin, 'dependencies')) + var error = false; + + _.each(plugin.dependencies, function(dep) { + try { + var a = require(dep.module); + } + catch(e) { + log.error('ERROR LOADING DEPENDENCY', dep.module); + + if(!e.message) { + log.error(e); + util.die(); + } + + if(!e.message.startsWith('Cannot find module')) + return util.die(e); + + error = [ + 'The plugin', + plugin.slug, + 'expects the module', + dep.module, + 'to be installed.', + 'However it is not, install', + 'it by running: \n\n', + '\tnpm install', + dep.module + '@' + dep.version + ].join(' '); + } + }); + + return error; + }, + // loads a plugin + // + // @param Object plugin + // plugin config object + // @param Function next + // callback + load: function(plugin, next) { + + plugin.config = config[plugin.slug]; + + if(!plugin.config || !plugin.config.enabled) + return next(); + + if(!_.contains(plugin.modes, gekkoMode)) { + log.warn( + 'The plugin', + plugin.name, + 'does not support the mode', + gekkoMode + '.', + 'It has been disabled.' + ) + return next(); + } + + log.info('Setting up:'); + log.info('\t', plugin.name); + log.info('\t', plugin.description); + + var cannotLoad = pluginHelper.cannotLoad(plugin); + if(cannotLoad) + return next(cannotLoad); + + if(plugin.path) + var Constructor = require(pluginDir + plugin.path(config)); + else + var Constructor = require(pluginDir + plugin.slug); + + if(plugin.async) { + inherits(Constructor, Emitter); + var instance = new Constructor(util.defer(function(err) { + next(err, instance); + }), plugin); + Emitter.call(instance); + + instance.meta = plugin; + } else { + inherits(Constructor, Emitter); + var instance = new Constructor(plugin); + Emitter.call(instance); + + instance.meta = plugin; + _.defer(function() { + next(null, instance); + }); + } + + if(!plugin.silent) + log.info('\n'); + } +} + +module.exports = pluginHelper; \ No newline at end of file diff --git a/core/prepareDateRange.js b/core/prepareDateRange.js new file mode 100644 index 0000000..9e85fce --- /dev/null +++ b/core/prepareDateRange.js @@ -0,0 +1,67 @@ +var _ = require('lodash'); +var prompt = require('prompt-lite'); +var moment = require('moment'); + +var util = require('./util'); +var config = util.getConfig(); +var dirs = util.dirs(); +var log = require(dirs.core + 'log'); + +var scan = require(dirs.tools + 'dateRangeScanner'); + +// helper to store the evenutally detected +// daterange. +var setDateRange = function(from, to) { + config.backtest.daterange = { + from: moment.unix(from).utc().format(), + to: moment.unix(to).utc().format(), + }; + util.setConfig(config); +} + + +module.exports = function(done) { + scan((err, ranges) => { + + if(_.size(ranges) === 0) + util.die('No history found for this market', true); + + if(_.size(ranges) === 1) { + var r = _.first(ranges); + log.info('Gekko was able to find a single daterange in the locally stored history:'); + log.info('\t', 'from:', moment.unix(r.from).utc().format('YYYY-MM-DD HH:mm:ss')); + log.info('\t', 'to:', moment.unix(r.to).utc().format('YYYY-MM-DD HH:mm:ss')); + + + setDateRange(r.from, r.to); + return done(); + } + + log.info( + 'Gekko detected multiple dateranges in the locally stored history.', + 'Please pick the daterange you are interested in testing:' + ); + + _.each(ranges, (range, i) => { + log.info('\t\t', `OPTION ${i + 1}:`); + log.info('\t', 'from:', moment.unix(range.from).utc().format('YYYY-MM-DD HH:mm:ss')); + log.info('\t', 'to:', moment.unix(range.to).utc().format('YYYY-MM-DD HH:mm:ss')); + }); + + prompt.get({name: 'option'}, (err, result) => { + + var option = parseInt(result.option); + if(option === NaN) + util.die('Not an option..', true); + + var range = ranges[option - 1]; + + if(!range) + util.die('Not an option..', true); + + setDateRange(range.from, range.to); + return done(); + }); + + }); +} \ No newline at end of file diff --git a/core/stats.js b/core/stats.js new file mode 100644 index 0000000..5c31492 --- /dev/null +++ b/core/stats.js @@ -0,0 +1,17 @@ +const stats = require('stats-lite'); +const lodash = require('lodash'); + + +// simply monkey patch the stats with other stuff we +// need and pass on. + +// sharpe ratio +// +// @param returns (array - list of returns) +// @param rfreturn (number - risk free return) +// +stats.sharpe = (returns, rfreturn) => { + return (stats.mean(returns) - rfreturn) / stats.stdev(returns); +} + +module.exports = stats; diff --git a/core/talib.js b/core/talib.js new file mode 100644 index 0000000..38fdaac --- /dev/null +++ b/core/talib.js @@ -0,0 +1,2297 @@ +var semver = require("semver"); +var _ = require('lodash'); + +// validate that talib is installed, if not we'll throw an exception which will +// prevent further loading or out outside this module +try { + var talib = require("talib"); +} catch (e) { + module.exports = null; + return; +} + +var talibError = 'Gekko was unable to configure talib indicator:\n\t'; +var talibGTEv103 = semver.gte(talib.version, '1.0.3'); + +// Wrapper that executes a talib indicator +var execute = function(callback, params) { + // talib callback style since talib-v1.0.3 + var talibCallback = function(err, result) { + if(err) return callback(err); + callback(null, result.result); + }; + + // talib legacy callback style before talib-v1.0.3 + var talibLegacyCallback = function(result) { + var error = result.error; + talibCallback.apply(this, [error, result]); + }; + + return talib.execute(params, talibGTEv103 ? talibCallback : talibLegacyCallback); +} + +// Helper that makes sure all required parameters +// for a specific talib indicator are present. +var verifyParams = (methodName, params) => { + var requiredParams = methods[methodName].requires; + + _.each(requiredParams, paramName => { + if(!_.has(params, paramName)) { + throw new Error(talibError + methodName + ' requires ' + paramName + '.'); + } + + var val = params[paramName]; + + if(!_.isNumber(val)) { + throw new Error(talibError + paramName + ' needs to be a number'); + } + }); +} + +var methods = {}; + + +//////////////////////////////Pattern Recognition////////////////////////////// +methods.cdl2crows = { + requires: [], + create: (params) => { + verifyParams('cdl2crows', params); + return (data, callback) => execute(callback, { + name: "CDL2CROWS", + startIdx: 0, + endIdx: data.close.length - 1, + open: data.open, + high: data.high, + low: data.low, + close: data.close + }); + } +} +methods.cdl3blackcrows = { + requires: [], + create: (params) => { + verifyParams('cdl3blackcrows', params); + return (data, callback) => execute(callback, { + name: "CDL3BLACKCROWS", + startIdx: 0, + endIdx: data.close.length - 1, + open: data.open, + high: data.high, + low: data.low, + close: data.close + }); + } +} +methods.cdl3inside = { + requires: [], + create: (params) => { + verifyParams('cdl3inside', params); + return (data, callback) => execute(callback, { + name: "CDL3INSIDE", + startIdx: 0, + endIdx: data.close.length - 1, + open: data.open, + high: data.high, + low: data.low, + close: data.close + }); + } +} +methods.cdl3linestrike = { + requires: [], + create: (params) => { + verifyParams('cdl3linestrike', params); + return (data, callback) => execute(callback, { + name: "CDL3LINESTRIKE", + startIdx: 0, + endIdx: data.close.length - 1, + open: data.open, + high: data.high, + low: data.low, + close: data.close + }); + } +} +methods.cdl3outside = { + requires: [], + create: (params) => { + verifyParams('cdl3outside', params); + return (data, callback) => execute(callback, { + name: "CDL3OUTSIDE", + startIdx: 0, + endIdx: data.close.length - 1, + open: data.open, + high: data.high, + low: data.low, + close: data.close + }); + } +} +methods.cdl3starsinsouth = { + requires: [], + create: (params) => { + verifyParams('cdl3starsinsouth', params); + return (data, callback) => execute(callback, { + name: "CDL3STARSINSOUTH", + startIdx: 0, + endIdx: data.close.length - 1, + open: data.open, + high: data.high, + low: data.low, + close: data.close + }); + } +} +methods.cdl3whitesoldiers = { + requires: [], + create: (params) => { + verifyParams('cdl3whitesoldiers', params); + return (data, callback) => execute(callback, { + name: "CDL3WHITESOLDIERS", + startIdx: 0, + endIdx: data.close.length - 1, + open: data.open, + high: data.high, + low: data.low, + close: data.close + }); + } +} +methods.cdlabandonedbaby = { + requires: ['optInPenetration'], + create: (params) => { + verifyParams('cdlabandonedbaby', params); + return (data, callback) => execute(callback, { + name: "CDLABANDONEDBABY", + startIdx: 0, + endIdx: data.close.length - 1, + open: data.open, + high: data.high, + low: data.low, + close: data.close, + optInPenetration: params.optInPenetration + }); + } +} +methods.cdladvanceblock = { + requires: [], + create: (params) => { + verifyParams('cdladvanceblock', params); + return (data, callback) => execute(callback, { + name: "CDLADVANCEBLOCK", + startIdx: 0, + endIdx: data.close.length - 1, + open: data.open, + high: data.high, + low: data.low, + close: data.close + }); + } +} +methods.cdlbelthold = { + requires: [], + create: (params) => { + verifyParams('cdlbelthold', params); + return (data, callback) => execute(callback, { + name: "CDLBELTHOLD", + startIdx: 0, + endIdx: data.close.length - 1, + open: data.open, + high: data.high, + low: data.low, + close: data.close + }); + } +} +methods.cdlbreakaway = { + requires: [], + create: (params) => { + verifyParams('cdlbreakaway', params); + return (data, callback) => execute(callback, { + name: "CDLBREAKAWAY", + startIdx: 0, + endIdx: data.close.length - 1, + open: data.open, + high: data.high, + low: data.low, + close: data.close + }); + } +} +methods.cdlclosingmarubozu = { + requires: [], + create: (params) => { + verifyParams('cdlclosingmarubozu', params); + return (data, callback) => execute(callback, { + name: "CDLCLOSINGMARUBOZU", + startIdx: 0, + endIdx: data.close.length - 1, + open: data.open, + high: data.high, + low: data.low, + close: data.close + }); + } +} +methods.cdlconcealbabyswall = { + requires: [], + create: (params) => { + verifyParams('cdlconcealbabyswall', params); + return (data, callback) => execute(callback, { + name: "CDLCONCEALBABYSWALL", + startIdx: 0, + endIdx: data.close.length - 1, + open: data.open, + high: data.high, + low: data.low, + close: data.close + }); + } +} +methods.cdlcounterattack = { + requires: [], + create: (params) => { + verifyParams('cdlcounterattack', params); + return (data, callback) => execute(callback, { + name: "CDLCOUNTERATTACK", + startIdx: 0, + endIdx: data.close.length - 1, + open: data.open, + high: data.high, + low: data.low, + close: data.close + }); + } +} +methods.cdldarkcloudcover = { + requires: ['optInPenetration'], + create: (params) => { + verifyParams('cdldarkcloudcover', params); + return (data, callback) => execute(callback, { + name: "CDLDARKCLOUDCOVER", + startIdx: 0, + endIdx: data.close.length - 1, + open: data.open, + high: data.high, + low: data.low, + close: data.close, + optInPenetration: params.optInPenetration + }); + } +} +methods.cdldoji = { + requires: [], + create: (params) => { + verifyParams('cdldoji', params); + return (data, callback) => execute(callback, { + name: "CDLDOJI", + startIdx: 0, + endIdx: data.close.length - 1, + open: data.open, + high: data.high, + low: data.low, + close: data.close + }); + } +} +methods.cdldojistar = { + requires: [], + create: (params) => { + verifyParams('cdldojistar', params); + return (data, callback) => execute(callback, { + name: "CDLDOJISTAR", + startIdx: 0, + endIdx: data.close.length - 1, + open: data.open, + high: data.high, + low: data.low, + close: data.close + }); + } +} +methods.cdldragonflydoji = { + requires: [], + create: (params) => { + verifyParams('cdldragonflydoji', params); + return (data, callback) => execute(callback, { + name: "CDLDRAGONFLYDOJI", + startIdx: 0, + endIdx: data.close.length - 1, + open: data.open, + high: data.high, + low: data.low, + close: data.close + }); + } +} +methods.cdlengulfing = { + requires: [], + create: (params) => { + verifyParams('cdlengulfing', params); + return (data, callback) => execute(callback, { + name: "CDLENGULFING", + startIdx: 0, + endIdx: data.close.length - 1, + open: data.open, + high: data.high, + low: data.low, + close: data.close + }); + } +} +methods.cdleveningdojistar = { + requires: ['optInPenetration'], + create: (params) => { + verifyParams('cdleveningdojistar', params); + return (data, callback) => execute(callback, { + name: "CDLEVENINGDOJISTAR", + startIdx: 0, + endIdx: data.close.length - 1, + open: data.open, + high: data.high, + low: data.low, + close: data.close, + optInPenetration: params.optInPenetration + }); + } +} +methods.cdleveningstar = { + requires: ['optInPenetration'], + create: (params) => { + verifyParams('cdleveningstar', params); + return (data, callback) => execute(callback, { + name: "CDLEVENINGSTAR", + startIdx: 0, + endIdx: data.close.length - 1, + open: data.open, + high: data.high, + low: data.low, + close: data.close, + optInPenetration: params.optInPenetration + }); + } +} +methods.cdlgapsidesidewhite = { + requires: [], + create: (params) => { + verifyParams('cdlgapsidesidewhite', params); + return (data, callback) => execute(callback, { + name: "CDLGAPSIDESIDEWHITE", + startIdx: 0, + endIdx: data.close.length - 1, + open: data.open, + high: data.high, + low: data.low, + close: data.close + }); + } +} +methods.cdlgravestonedoji = { + requires: [], + create: (params) => { + verifyParams('cdlgravestonedoji', params); + return (data, callback) => execute(callback, { + name: "CDLGRAVESTONEDOJI", + startIdx: 0, + endIdx: data.close.length - 1, + open: data.open, + high: data.high, + low: data.low, + close: data.close + }); + } +} +methods.cdlhammer = { + requires: [], + create: (params) => { + verifyParams('cdlhammer', params); + return (data, callback) => execute(callback, { + name: "CDLHAMMER", + startIdx: 0, + endIdx: data.close.length - 1, + open: data.open, + high: data.high, + low: data.low, + close: data.close + }); + } +} +methods.cdlhangingman = { + requires: [], + create: (params) => { + verifyParams('cdlhangingman', params); + return (data, callback) => execute(callback, { + name: "CDLHANGINGMAN", + startIdx: 0, + endIdx: data.close.length - 1, + open: data.open, + high: data.high, + low: data.low, + close: data.close + }); + } +} +methods.cdlharami = { + requires: [], + create: (params) => { + verifyParams('cdlharami', params); + return (data, callback) => execute(callback, { + name: "CDLHARAMI", + startIdx: 0, + endIdx: data.close.length - 1, + open: data.open, + high: data.high, + low: data.low, + close: data.close + }); + } +} +methods.cdlharamicross = { + requires: [], + create: (params) => { + verifyParams('cdlharamicross', params); + return (data, callback) => execute(callback, { + name: "CDLHARAMICROSS", + startIdx: 0, + endIdx: data.close.length - 1, + open: data.open, + high: data.high, + low: data.low, + close: data.close + }); + } +} +methods.cdlhighwave = { + requires: [], + create: (params) => { + verifyParams('cdlhighwave', params); + return (data, callback) => execute(callback, { + name: "CDLHIGHWAVE", + startIdx: 0, + endIdx: data.close.length - 1, + open: data.open, + high: data.high, + low: data.low, + close: data.close + }); + } +} +methods.cdlhikkake = { + requires: [], + create: (params) => { + verifyParams('cdlhikkake', params); + return (data, callback) => execute(callback, { + name: "CDLHIKKAKE", + startIdx: 0, + endIdx: data.close.length - 1, + open: data.open, + high: data.high, + low: data.low, + close: data.close + }); + } +} +methods.cdlhikkakemod = { + requires: [], + create: (params) => { + verifyParams('cdlhikkakemod', params); + return (data, callback) => execute(callback, { + name: "CDLHIKKAKEMOD", + startIdx: 0, + endIdx: data.close.length - 1, + open: data.open, + high: data.high, + low: data.low, + close: data.close + }); + } +} +methods.cdlhomingpigeon = { + requires: [], + create: (params) => { + verifyParams('cdlhomingpigeon', params); + return (data, callback) => execute(callback, { + name: "CDLHOMINGPIGEON", + startIdx: 0, + endIdx: data.close.length - 1, + open: data.open, + high: data.high, + low: data.low, + close: data.close + }); + } +} +methods.cdlidentical3crows = { + requires: [], + create: (params) => { + verifyParams('cdlidentical3crows', params); + return (data, callback) => execute(callback, { + name: "CDLIDENTICAL3CROWS", + startIdx: 0, + endIdx: data.close.length - 1, + open: data.open, + high: data.high, + low: data.low, + close: data.close + }); + } +} +methods.cdlinneck = { + requires: [], + create: (params) => { + verifyParams('cdlinneck', params); + return (data, callback) => execute(callback, { + name: "CDLINNECK", + startIdx: 0, + endIdx: data.close.length - 1, + open: data.open, + high: data.high, + low: data.low, + close: data.close + }); + } +} +methods.cdlinvertedhammer = { + requires: [], + create: (params) => { + verifyParams('cdlinvertedhammer', params); + return (data, callback) => execute(callback, { + name: "CDLINVERTEDHAMMER", + startIdx: 0, + endIdx: data.close.length - 1, + open: data.open, + high: data.high, + low: data.low, + close: data.close + }); + } +} +methods.cdlkicking = { + requires: [], + create: (params) => { + verifyParams('cdlkicking', params); + return (data, callback) => execute(callback, { + name: "CDLKICKING", + startIdx: 0, + endIdx: data.close.length - 1, + open: data.open, + high: data.high, + low: data.low, + close: data.close + }); + } +} +methods.cdlkickingbylength = { + requires: [], + create: (params) => { + verifyParams('cdlkickingbylength', params); + return (data, callback) => execute(callback, { + name: "CDLKICKINGBYLENGTH", + startIdx: 0, + endIdx: data.close.length - 1, + open: data.open, + high: data.high, + low: data.low, + close: data.close + }); + } +} +methods.cdlladderbottom = { + requires: [], + create: (params) => { + verifyParams('cdlladderbottom', params); + return (data, callback) => execute(callback, { + name: "CDLLADDERBOTTOM", + startIdx: 0, + endIdx: data.close.length - 1, + open: data.open, + high: data.high, + low: data.low, + close: data.close + }); + } +} +methods.cdllongleggeddoji = { + requires: [], + create: (params) => { + verifyParams('cdllongleggeddoji', params); + return (data, callback) => execute(callback, { + name: "CDLLONGLEGGEDDOJI", + startIdx: 0, + endIdx: data.close.length - 1, + open: data.open, + high: data.high, + low: data.low, + close: data.close + }); + } +} +methods.cdllongline = { + requires: [], + create: (params) => { + verifyParams('cdllongline', params); + return (data, callback) => execute(callback, { + name: "CDLLONGLINE", + startIdx: 0, + endIdx: data.close.length - 1, + open: data.open, + high: data.high, + low: data.low, + close: data.close + }); + } +} +methods.cdlmarubozu = { + requires: [], + create: (params) => { + verifyParams('cdlmarubozu', params); + return (data, callback) => execute(callback, { + name: "CDLMARUBOZU", + startIdx: 0, + endIdx: data.close.length - 1, + open: data.open, + high: data.high, + low: data.low, + close: data.close + }); + } +} +methods.cdlmatchinglow = { + requires: [], + create: (params) => { + verifyParams('cdlmatchinglow', params); + return (data, callback) => execute(callback, { + name: "CDLMATCHINGLOW", + startIdx: 0, + endIdx: data.close.length - 1, + open: data.open, + high: data.high, + low: data.low, + close: data.close + }); + } +} +methods.cdlmathold = { + requires: ['optInPenetration'], + create: (params) => { + verifyParams('cdlmathold', params); + return (data, callback) => execute(callback, { + name: "CDLMATHOLD", + startIdx: 0, + endIdx: data.close.length - 1, + open: data.open, + high: data.high, + low: data.low, + close: data.close, + optInPenetration: params.optInPenetration + }); + } +} +methods.cdlmorningdojistar = { + requires: ['optInPenetration'], + create: (params) => { + verifyParams('cdlmorningdojistar', params); + return (data, callback) => execute(callback, { + name: "CDLMORNINGDOJISTAR", + startIdx: 0, + endIdx: data.close.length - 1, + open: data.open, + high: data.high, + low: data.low, + close: data.close, + optInPenetration: params.optInPenetration + }); + } +} +methods.cdlmorningstar = { + requires: ['optInPenetration'], + create: (params) => { + verifyParams('cdlmorningstar', params); + return (data, callback) => execute(callback, { + name: "CDLMORNINGSTAR", + startIdx: 0, + endIdx: data.close.length - 1, + open: data.open, + high: data.high, + low: data.low, + close: data.close, + optInPenetration: params.optInPenetration + }); + } +} +methods.cdlonneck = { + requires: [], + create: (params) => { + verifyParams('cdlonneck', params); + return (data, callback) => execute(callback, { + name: "CDLONNECK", + startIdx: 0, + endIdx: data.close.length - 1, + open: data.open, + high: data.high, + low: data.low, + close: data.close + }); + } +} +methods.cdlpiercing = { + requires: [], + create: (params) => { + verifyParams('cdlpiercing', params); + return (data, callback) => execute(callback, { + name: "CDLPIERCING", + startIdx: 0, + endIdx: data.close.length - 1, + open: data.open, + high: data.high, + low: data.low, + close: data.close + }); + } +} +methods.cdlrickshawman = { + requires: [], + create: (params) => { + verifyParams('cdlrickshawman', params); + return (data, callback) => execute(callback, { + name: "CDLRICKSHAWMAN", + startIdx: 0, + endIdx: data.close.length - 1, + open: data.open, + high: data.high, + low: data.low, + close: data.close + }); + } +} +methods.cdlrisefall3methods = { + requires: [], + create: (params) => { + verifyParams('cdlrisefall3methods', params); + return (data, callback) => execute(callback, { + name: "CDLRISEFALL3METHODS", + startIdx: 0, + endIdx: data.close.length - 1, + open: data.open, + high: data.high, + low: data.low, + close: data.close + }); + } +} +methods.cdlseparatinglines = { + requires: [], + create: (params) => { + verifyParams('cdlseparatinglines', params); + return (data, callback) => execute(callback, { + name: "CDLSEPARATINGLINES", + startIdx: 0, + endIdx: data.close.length - 1, + open: data.open, + high: data.high, + low: data.low, + close: data.close + }); + } +} +methods.cdlshootingstar = { + requires: [], + create: (params) => { + verifyParams('cdlshootingstar', params); + return (data, callback) => execute(callback, { + name: "CDLSHOOTINGSTAR", + startIdx: 0, + endIdx: data.close.length - 1, + open: data.open, + high: data.high, + low: data.low, + close: data.close + }); + } +} +methods.cdlshortline = { + requires: [], + create: (params) => { + verifyParams('cdlshortline', params); + return (data, callback) => execute(callback, { + name: "CDLSHORTLINE", + startIdx: 0, + endIdx: data.close.length - 1, + open: data.open, + high: data.high, + low: data.low, + close: data.close + }); + } +} +methods.cdlspinningtop = { + requires: [], + create: (params) => { + verifyParams('cdlspinningtop', params); + return (data, callback) => execute(callback, { + name: "CDLSPINNINGTOP", + startIdx: 0, + endIdx: data.close.length - 1, + open: data.open, + high: data.high, + low: data.low, + close: data.close + }); + } +} +methods.cdlstalledpattern = { + requires: [], + create: (params) => { + verifyParams('cdlstalledpattern', params); + return (data, callback) => execute(callback, { + name: "CDLSTALLEDPATTERN", + startIdx: 0, + endIdx: data.close.length - 1, + open: data.open, + high: data.high, + low: data.low, + close: data.close + }); + } +} +methods.cdlsticksandwich = { + requires: [], + create: (params) => { + verifyParams('cdlsticksandwich', params); + return (data, callback) => execute(callback, { + name: "CDLSTICKSANDWICH", + startIdx: 0, + endIdx: data.close.length - 1, + open: data.open, + high: data.high, + low: data.low, + close: data.close + }); + } +} +methods.cdltakuri = { + requires: [], + create: (params) => { + verifyParams('cdltakuri', params); + return (data, callback) => execute(callback, { + name: "CDLTAKURI", + startIdx: 0, + endIdx: data.close.length - 1, + open: data.open, + high: data.high, + low: data.low, + close: data.close + }); + } +} +methods.cdltasukigap = { + requires: [], + create: (params) => { + verifyParams('cdltasukigap', params); + return (data, callback) => execute(callback, { + name: "CDLTASUKIGAP", + startIdx: 0, + endIdx: data.close.length - 1, + open: data.open, + high: data.high, + low: data.low, + close: data.close + }); + } +} +methods.cdlthrusting = { + requires: [], + create: (params) => { + verifyParams('cdlthrusting', params); + return (data, callback) => execute(callback, { + name: "CDLTHRUSTING", + startIdx: 0, + endIdx: data.close.length - 1, + open: data.open, + high: data.high, + low: data.low, + close: data.close + }); + } +} +methods.cdltristar = { + requires: [], + create: (params) => { + verifyParams('cdltristar', params); + return (data, callback) => execute(callback, { + name: "CDLTRISTAR", + startIdx: 0, + endIdx: data.close.length - 1, + open: data.open, + high: data.high, + low: data.low, + close: data.close + }); + } +} +methods.cdlunique3river = { + requires: [], + create: (params) => { + verifyParams('cdlunique3river', params); + return (data, callback) => execute(callback, { + name: "CDLUNIQUE3RIVER", + startIdx: 0, + endIdx: data.close.length - 1, + open: data.open, + high: data.high, + low: data.low, + close: data.close + }); + } +} +methods.cdlupsidegap2crows = { + requires: [], + create: (params) => { + verifyParams('cdlupsidegap2crows', params); + return (data, callback) => execute(callback, { + name: "CDLUPSIDEGAP2CROWS", + startIdx: 0, + endIdx: data.close.length - 1, + open: data.open, + high: data.high, + low: data.low, + close: data.close + }); + } +} +methods.cdlxsidegap3methods = { + requires: [], + create: (params) => { + verifyParams('cdlxsidegap3methods', params); + return (data, callback) => execute(callback, { + name: "CDLXSIDEGAP3METHODS", + startIdx: 0, + endIdx: data.close.length - 1, + open: data.open, + high: data.high, + low: data.low, + close: data.close + }); + } +} + +//////////////////////////////Pattern Recognition////////////////////////////// + +methods.accbands = { + requires: ['optInTimePeriod'], + create: (params) => { + verifyParams('accbands', params); + + return (data, callback) => execute(callback, { + name: "ACCBANDS", + high: data.high, + low: data.low, + close: data.close, + startIdx: 0, + endIdx: data.high.length - 1, + optInTimePeriod: params.optInTimePeriod + }); + } +} + +methods.ad = { + requires: ['optInTimePeriod'], + create: (params) => { + verifyParams('accbands', params); + + return (data, callback) => execute(callback, { + name: "AD", + high: data.high, + low: data.low, + close: data.close, + volume: data.volume, + startIdx: 0, + endIdx: data.high.length - 1, + optInTimePeriod: params.optInTimePeriod + }); + } +} + +methods.adosc = { + requires: ['optInFastPeriod', 'optInSlowPeriod'], + create: (params) => { + verifyParams('adosc', params); + + return (data, callback) => execute(callback, { + name: "ADOSC", + high: data.high, + low: data.low, + close: data.close, + volume: data.volume, + startIdx: 0, + endIdx: data.high.length - 1, + optInFastPeriod: params.optInFastPeriod, + optInSlowPeriod: params.optInSlowPeriod + }); + } +} + +methods.adx = { + requires: ['optInTimePeriod'], + create: (params) => { + verifyParams('adx', params); + + return (data, callback) => execute(callback, { + name: "ADX", + high: data.high, + low: data.low, + close: data.close, + startIdx: 0, + endIdx: data.high.length - 1, + optInTimePeriod: params.optInTimePeriod + }); + } +} + +methods.adxr = { + requires: ['optInTimePeriod'], + create: (params) => { + verifyParams('adxr', params); + + return (data, callback) => execute(callback, { + name: "ADXR", + high: data.high, + low: data.low, + close: data.close, + startIdx: 0, + endIdx: data.high.length - 1, + optInTimePeriod: params.optInTimePeriod + }); + } +} + +methods.apo = { + requires: ['optInFastPeriod', 'optInSlowPeriod', 'optInMAType'], + create: (params) => { + verifyParams('apo', params); + + return (data, callback) => execute(callback, { + name: "APO", + inReal: data.close, + startIdx: 0, + endIdx: data.length - 1, + optInFastPeriod: params.optInFastPeriod, + optInSlowPeriod: params.optInSlowPeriod, + optInMAType: params.optInMAType + }); + } +} + +methods.aroon = { + requires: ['optInTimePeriod'], + create: (params) => { + verifyParams('aroon', params); + + return (data, callback) => execute(callback, { + name: "AROON", + high: data.high, + low: data.low, + startIdx: 0, + endIdx: data.high.length - 1, + optInTimePeriod: params.optInTimePeriod + }); + } +} + +methods.aroonosc = { + requires: ['optInTimePeriod'], + create: (params) => { + verifyParams('aroonosc', params); + + return (data, callback) => execute(callback, { + name: "AROONOSC", + high: data.high, + low: data.low, + startIdx: 0, + endIdx: data.high.length - 1, + optInTimePeriod: params.optInTimePeriod + }); + } +} + +methods.atr = { + requires: ['optInTimePeriod'], + create: (params) => { + verifyParams('atr', params); + + return (data, callback) => execute(callback, { + name: "ATR", + high: data.high, + low: data.low, + close: data.close, + startIdx: 0, + endIdx: data.high.length - 1, + optInTimePeriod: params.optInTimePeriod + }); + } +} + +methods.avgprice = { + requires: ['optInTimePeriod'], + create: (params) => { + verifyParams('avgprice', params); + + return (data, callback) => execute(callback, { + name: "AVGPRICE", + open: data.open, + high: data.high, + low: data.low, + close: data.close, + startIdx: 0, + endIdx: data.open.length - 1, + optInTimePeriod: params.optInTimePeriod + }); + } +} + +methods.bbands = { + requires: ['optInTimePeriod', 'optInNbDevUp', 'optInNbDevDn', 'optInMAType'], + create: (params) => { + verifyParams('bbands', params); + + return (data, callback) => execute(callback, { + name: "BBANDS", + inReal: data.close, + startIdx: 0, + endIdx: data.close.length - 1, + optInTimePeriod: params.optInTimePeriod, + optInNbDevUp: params.optInNbDevUp, + optInNbDevDn: params.optInNbDevDn, + optInMAType: params.optInMAType + }); + } +} + +/////////////////////////////////////////////////////////////// + + +// this.beta = function(data_0, data_1, period) { +// return talibWrapper({ +// name: "BETA", +// inReal0: data_0, +// inReal1: data_1, +// startIdx: 0, +// endIdx: data_0.length - 1, +// optInTimePeriod: period +// }); +// }; + +methods.bop = { + requires: [], + create: (params) => { + verifyParams('bop', params); + + return (data, callback) => execute(callback, { + name: "BOP", + open: data.open, + high: data.high, + low: data.low, + close: data.close, + startIdx: 0, + endIdx: data.high.length - 1 + }); + } +} + +methods.cci = { + requires: ['optInTimePeriod'], + create: (params) => { + verifyParams('cci', params); + + return (data, callback) => execute(callback, { + name: "CCI", + high: data.high, + low: data.low, + close: data.close, + startIdx: 0, + endIdx: data.high.length - 1, + optInTimePeriod: params.optInTimePeriod + }); + } +} + +methods.cmo = { + requires: ['optInTimePeriod'], + create: (params) => { + verifyParams('cmo', params); + + return (data, callback) => execute(callback, { + name: "CMO", + inReal: data.close, + startIdx: 0, + endIdx: data.close.length - 1, + optInTimePeriod: params.optInTimePeriod + }); + } +} + +// this.correl = function(data_0, data_1, period) { +// return talibWrapper({ +// name: "CORREL", +// inReal0: data_0, +// inReal1: data_1, +// startIdx: 0, +// endIdx: data_0.length - 1, +// optInTimePeriod: period +// }); +// }; + +methods.dema = { + requires: ['optInTimePeriod'], + create: (params) => { + verifyParams('dema', params); + + return (data, callback) => execute(callback, { + name: "DEMA", + inReal: data.close, + startIdx: 0, + endIdx: data.close.length - 1, + optInTimePeriod: params.optInTimePeriod + }); + } +} + +methods.dx = { + requires: ['optInTimePeriod'], + create: (params) => { + verifyParams('dx', params); + + return (data, callback) => execute(callback, { + name: "DX", + high: data.high, + low: data.low, + close: data.close, + startIdx: 0, + endIdx: data.high.length - 1, + optInTimePeriod: params.optInTimePeriod + }); + } +} + +methods.ema = { + requires: ['optInTimePeriod'], + create: (params) => { + verifyParams('ema', params); + + return (data, callback) => execute(callback, { + name: "EMA", + inReal: data.close, + startIdx: 0, + endIdx: data.close.length - 1, + optInTimePeriod: params.optInTimePeriod + }); + } +} + +methods.ht_dcperiod = { + requires: [], + create: (params) => { + verifyParams('ht_dcperiod', params); + + return (data, callback) => execute(callback, { + name: "HT_DCPERIOD", + inReal: data.close, + startIdx: 0, + endIdx: data.close.length - 1 + }); + } +} + +methods.ht_dcphase = { + requires: [], + create: (params) => { + verifyParams('ht_dcphase', params); + + return (data, callback) => execute(callback, { + name: "HT_DCPHASE", + inReal: data.close, + startIdx: 0, + endIdx: data.close.length - 1 + }); + } +} + +methods.ht_phasor = { + requires: [], + create: (params) => { + verifyParams('ht_phasor', params); + + return (data, callback) => execute(callback, { + name: "HT_PHASOR", + inReal: data.close, + startIdx: 0, + endIdx: data.close.length - 1 + }); + } +} + +methods.ht_sine = { + requires: [], + create: (params) => { + verifyParams('ht_sine', params); + + return (data, callback) => execute(callback, { + name: "HT_SINE", + inReal: data.close, + startIdx: 0, + endIdx: data.close.length - 1 + }); + } +} + + +methods.ht_trendline = { + requires: [], + create: (params) => { + verifyParams('ht_trendline', params); + + return (data, callback) => execute(callback, { + name: "HT_TRENDLINE", + inReal: data.close, + startIdx: 0, + endIdx: data.close.length - 1 + }); + } +} + +methods.ht_trendmode = { + requires: [], + create: (params) => { + verifyParams('ht_trendmode', params); + + return (data, callback) => execute(callback, { + name: "HT_TRENDMODE", + inReal: data.close, + startIdx: 0, + endIdx: data.close.length - 1 + }); + } +} + +methods.imi = { + requires: ['optInTimePeriod'], + create: (params) => { + verifyParams('imi', params); + + return (data, callback) => execute(callback, { + name: "IMI", + open: data.open, + close: data.close, + startIdx: 0, + endIdx: data.open.length - 1, + optInTimePeriod: params.optInTimePeriod + }); + } +} + +methods.kama = { + requires: ['optInTimePeriod'], + create: (params) => { + verifyParams('kama', params); + + return (data, callback) => execute(callback, { + name: "KAMA", + inReal: data.close, + startIdx: 0, + endIdx: data.close.length - 1, + optInTimePeriod: params.optInTimePeriod + }); + } +} + +methods.linearreg = { + requires: ['optInTimePeriod'], + create: (params) => { + verifyParams('linearreg', params); + + return (data, callback) => execute(callback, { + name: "LINEARREG", + inReal: data.close, + startIdx: 0, + endIdx: data.close.length - 1, + optInTimePeriod: params.optInTimePeriod + }); + } +} + +methods.linearreg_angle = { + requires: ['optInTimePeriod'], + create: (params) => { + verifyParams('linearreg_angle', params); + + return (data, callback) => execute(callback, { + name: "LINEARREG_ANGLE", + inReal: data.close, + startIdx: 0, + endIdx: data.close.length - 1, + optInTimePeriod: params.optInTimePeriod + }); + } +} + +methods.linearreg_intercept = { + requires: ['optInTimePeriod'], + create: (params) => { + verifyParams('linearreg_intercept', params); + + return (data, callback) => execute(callback, { + name: "LINEARREG_INTERCEPT", + inReal: data.close, + startIdx: 0, + endIdx: data.close.length - 1, + optInTimePeriod: params.optInTimePeriod + }); + } +} + +methods.linearreg_slope = { + requires: ['optInTimePeriod'], + create: (params) => { + verifyParams('linearreg_slope', params); + + return (data, callback) => execute(callback, { + name: "LINEARREG_SLOPE", + inReal: data.close, + startIdx: 0, + endIdx: data.close.length - 1, + optInTimePeriod: params.optInTimePeriod + }); + } +} + +methods.ma = { + requires: ['optInTimePeriod', 'optInMAType'], + create: (params) => { + verifyParams('ma', params); + + return (data, callback) => execute(callback, { + name: "MA", + inReal: data.close, + startIdx: 0, + endIdx: data.close.length - 1, + optInTimePeriod: params.optInTimePeriod, + optInMAType: params.optInMAType + }); + } +} + +methods.macd = { + requires: ['optInFastPeriod', 'optInSlowPeriod', 'optInSignalPeriod'], + create: (params) => { + verifyParams('macd', params); + + return (data, callback) => execute(callback, { + name: "MACD", + inReal: data.close, + startIdx: 0, + endIdx: data.close.length - 1, + optInFastPeriod: params.optInFastPeriod, + optInSlowPeriod: params.optInSlowPeriod, + optInSignalPeriod: params.optInSignalPeriod + }); + } +} + +methods.macdext = { + requires: [ + 'optInFastPeriod', + 'optInFastMAType', + 'optInSlowPeriod', + 'optInSlowMAType', + 'optInSignalPeriod', + 'optInSignalMAType' + ], + create: (params) => { + verifyParams('macdext', params); + + return (data, callback) => execute(callback, { + name: "MACDEXT", + inReal: data.close, + startIdx: 0, + endIdx: data.close.length - 1, + optInFastPeriod: params.optInFastPeriod, + optInFastMAType: params.optInFastMAType, + optInSlowPeriod: params.optInSlowPeriod, + optInSlowMAType: params.optInSlowMAType, + optInSignalPeriod: params.optInSignalPeriod, + optInSignalMAType: params.optInSignalMAType + }); + } +} + +methods.macdfix = { + requires: ['optInSignalPeriod'], + create: (params) => { + verifyParams('macdfix', params); + + return (data, callback) => execute(callback, { + name: "MACDFIX", + inReal: data.close, + startIdx: 0, + endIdx: data.close.length - 1, + optInSignalPeriod: params.optInSignalPeriod + }); + } +} + +methods.mama = { + requires: ['optInFastLimit', 'optInSlowLimit'], + create: (params) => { + verifyParams('mama', params); + + return (data, callback) => execute(callback, { + name: "MAMA", + inReal: data.close, + startIdx: 0, + endIdx: data.close.length - 1, + optInFastLimit: params.optInFastLimit, + optInSlowLimit: params.optInSlowLimit + }); + } +} + +methods.mavp = { + requires: ['inPeriods', 'optInMinPeriod', 'optInMaxPeriod', 'optInMAType'], + create: (params) => { + verifyParams('mavp', params); + + return (data, callback) => execute(callback, { + name: "MAVP", + inReal: data.close, + inPeriods: params.inPeriods, + startIdx: 0, + endIdx: data.close.length - 1, + optInMinPeriod: params.optInMinPeriod, + optInMaxPeriod: params.optInMaxPeriod, + optInMAType: params.optInMAType + }); + } +} + + +methods.max = { + requires: ['optInTimePeriod'], + create: (params) => { + verifyParams('max', params); + + return (data, callback) => execute(callback, { + name: "MAX", + inReal: data.close, + startIdx: 0, + endIdx: data.close.length - 1, + optInTimePeriod: params.optInTimePeriod + }); + } +} + + +methods.maxindex = { + requires: ['optInTimePeriod'], + create: (params) => { + verifyParams('maxindex', params); + + return (data, callback) => execute(callback, { + name: "MAXINDEX", + inReal: data.close, + startIdx: 0, + endIdx: data.close.length - 1, + optInTimePeriod: params.optInTimePeriod + }); + } +} + +methods.medprice = { + requires: ['optInTimePeriod'], + create: (params) => { + verifyParams('medprice', params); + + return (data, callback) => execute(callback, { + name: "MEDPRICE", + high: data.high, + low: data.low, + startIdx: 0, + endIdx: data.high.length - 1, + optInTimePeriod: params.optInTimePeriod + }); + } +} + +methods.mfi = { + requires: ['optInTimePeriod'], + create: (params) => { + verifyParams('mfi', params); + + return (data, callback) => execute(callback, { + name: "MFI", + high: data.high, + low: data.low, + close: data.close, + volume: data.volume, + startIdx: 0, + endIdx: data.high.length - 1, + optInTimePeriod: params.optInTimePeriod + }); + } +} + +methods.midpoint = { + requires: ['optInTimePeriod'], + create: (params) => { + verifyParams('midpoint', params); + + return (data, callback) => execute(callback, { + name: "MIDPOINT", + inReal: data.close, + startIdx: 0, + endIdx: data.close.length - 1, + optInTimePeriod: params.optInTimePeriod + }); + } +} + +methods.midprice = { + requires: ['optInTimePeriod'], + create: (params) => { + verifyParams('midprice', params); + + return (data, callback) => execute(callback, { + name: "MIDPRICE", + high: data.high, + low: data.low, + startIdx: 0, + endIdx: data.high.length - 1, + optInTimePeriod: params.optInTimePeriod + }); + } +} + +methods.min = { + requires: ['optInTimePeriod'], + create: (params) => { + verifyParams('min', params); + + return (data, callback) => execute(callback, { + name: "MIN", + inReal: data.close, + startIdx: 0, + endIdx: data.close.length - 1, + optInTimePeriod: params.optInTimePeriod + }); + } +} + +methods.minindex = { + requires: ['optInTimePeriod'], + create: (params) => { + verifyParams('minindex', params); + + return (data, callback) => execute(callback, { + name: "MININDEX", + inReal: data.close, + startIdx: 0, + endIdx: data.close.length - 1, + optInTimePeriod: params.optInTimePeriod + }); + } +} + +methods.minmax = { + requires: ['optInTimePeriod'], + create: (params) => { + verifyParams('minmax', params); + + return (data, callback) => execute(callback, { + name: "MINMAX", + inReal: data.close, + startIdx: 0, + endIdx: data.close.length - 1, + optInTimePeriod: params.optInTimePeriod + }); + } +} + +methods.minmaxindex = { + requires: ['optInTimePeriod'], + create: (params) => { + verifyParams('minmaxindex', params); + + return (data, callback) => execute(callback, { + name: "MINMAXINDEX", + inReal: data.close, + startIdx: 0, + endIdx: data.length - 1, + optInTimePeriod: params.optInTimePeriod + }); + } +} + +methods.minus_di = { + requires: ['optInTimePeriod'], + create: (params) => { + verifyParams('minus_di', params); + + return (data, callback) => execute(callback, { + name: "MINUS_DI", + high: data.high, + low: data.low, + close: data.close, + startIdx: 0, + endIdx: data.high.length - 1, + optInTimePeriod: params.optInTimePeriod + }); + } +} + +methods.minus_dm = { + requires: ['optInTimePeriod'], + create: (params) => { + verifyParams('minus_dm', params); + + return (data, callback) => execute(callback, { + name: "MINUS_DM", + high: data.high, + low: data.low, + startIdx: 0, + endIdx: data.high.length - 1, + optInTimePeriod: params.optInTimePeriod + }); + } +} + +methods.mom = { + requires: ['optInTimePeriod'], + create: (params) => { + verifyParams('mom', params); + + return (data, callback) => execute(callback, { + name: "MOM", + inReal: data.close, + startIdx: 0, + endIdx: data.close.length - 1, + optInTimePeriod: params.optInTimePeriod + }); + } +} + +methods.natr = { + requires: ['optInTimePeriod'], + create: (params) => { + verifyParams('natr', params); + + return (data, callback) => execute(callback, { + name: "NATR", + high: data.high, + low: data.low, + close: data.close, + startIdx: 0, + endIdx: data.high.length - 1, + optInTimePeriod: params.optInTimePeriod + }); + } +} + +methods.obv = { + requires: [], + create: (params) => { + verifyParams('obv', params); + + return (data, callback) => execute(callback, { + name: "OBV", + inReal: data.close, + volume: data.volume, + startIdx: 0, + endIdx: data.close.length - 1 + }); + } +} + +methods.plus_di = { + requires: ['optInTimePeriod'], + create: (params) => { + verifyParams('plus_di', params); + + return (data, callback) => execute(callback, { + name: "PLUS_DI", + high: data.high, + low: data.low, + close: data.close, + startIdx: 0, + endIdx: data.high.length - 1, + optInTimePeriod: params.optInTimePeriod + }); + } +} + +methods.plus_dm = { + requires: ['optInTimePeriod'], + create: (params) => { + verifyParams('plus_dm', params); + + return (data, callback) => execute(callback, { + name: "PLUS_DM", + high: data.high, + low: data.low, + startIdx: 0, + endIdx: data.high.length - 1, + optInTimePeriod: params.optInTimePeriod + }); + } +} + +methods.ppo = { + requires: ['optInFastPeriod', 'optInSlowPeriod', 'optInMAType'], + create: (params) => { + verifyParams('ppo', params); + + return (data, callback) => execute(callback, { + name: "PPO", + inReal: data.close, + startIdx: 0, + endIdx: data.close.length - 1, + optInFastPeriod: params.optInFastPeriod, + optInSlowPeriod: params.optInSlowPeriod, + optInMAType: params.optInMAType + }); + } +} + +methods.roc = { + requires: ['optInTimePeriod'], + create: (params) => { + verifyParams('roc', params); + + return (data, callback) => execute(callback, { + name: "ROC", + inReal: data.close, + startIdx: 0, + endIdx: data.close.length - 1, + optInTimePeriod: params.optInTimePeriod + }); + } +} + +methods.rocp = { + requires: ['optInTimePeriod'], + create: (params) => { + verifyParams('rocp', params); + + return (data, callback) => execute(callback, { + name: "ROCP", + inReal: data.close, + startIdx: 0, + endIdx: data.close.length - 1, + optInTimePeriod: params.optInTimePeriod + }); + } +} + +methods.rocr = { + requires: ['optInTimePeriod'], + create: (params) => { + verifyParams('rocr', params); + + return (data, callback) => execute(callback, { + name: "ROCR", + inReal: data.close, + startIdx: 0, + endIdx: data.close.length - 1, + optInTimePeriod: params.optInTimePeriod + }); + } +} + +methods.rocr100 = { + requires: ['optInTimePeriod'], + create: (params) => { + verifyParams('rocr100', params); + + return (data, callback) => execute(callback, { + name: "ROCR100", + inReal: data.close, + startIdx: 0, + endIdx: data.close.length - 1, + optInTimePeriod: params.optInTimePeriod + }); + } +} + +methods.rsi = { + requires: ['optInTimePeriod'], + create: (params) => { + verifyParams('rsi', params); + + return (data, callback) => execute(callback, { + name: "RSI", + inReal: data.close, + startIdx: 0, + endIdx: data.close.length - 1, + optInTimePeriod: params.optInTimePeriod + }); + } +} + +methods.sar = { + requires: ['optInAcceleration', 'optInMaximum'], + create: (params) => { + verifyParams('sar', params); + + return (data, callback) => execute(callback, { + name: "SAR", + high: data.high, + low: data.low, + startIdx: 0, + endIdx: data.high.length - 1, + optInAcceleration: params.optInAcceleration, + optInMaximum: params.optInMaximum + }); + } +} + +methods.sarext = { + requires: [ + 'optInStartValue', + 'optInOffsetOnReverse', + 'optInAccelerationInitLong', + 'optInAccelerationLong', + 'optInAccelerationMaxLong', + 'optInAccelerationInitShort', + 'optInAccelerationShort', + 'optInAccelerationMaxShort' + + ], + create: (params) => { + verifyParams('sarext', params); + + return (data, callback) => execute(callback, { + name: "SAREXT", + high: data.high, + low: data.low, + startIdx: 0, + endIdx: data.high.length - 1, + + optInStartValue: params.optInStartValue, + optInOffsetOnReverse: params.optInOffsetOnReverse, + optInAccelerationInitLong: params.optInAccelerationInitLong, + optInAccelerationLong: params.optInAccelerationLong, + optInAccelerationMaxLong: params.optInAccelerationMaxLong, + optInAccelerationInitShort: params.optInAccelerationInitShort, + optInAccelerationShort: params.optInAccelerationShort, + optInAccelerationMaxShort: params.optInAccelerationMaxShort + }); + } +} + +methods.sma = { + requires: ['optInTimePeriod'], + create: (params) => { + verifyParams('sma', params); + + return (data, callback) => execute(callback, { + name: "SMA", + inReal: data.close, + startIdx: 0, + endIdx: data.close.length - 1, + optInTimePeriod: params.optInTimePeriod + }); + } +} + +methods.stddev = { + requires: ['optInTimePeriod', 'optInNbDev'], + create: (params) => { + verifyParams('stddev', params); + + return (data, callback) => execute(callback, { + name: "STDDEV", + inReal: data.close, + startIdx: 0, + endIdx: data.close.length - 1, + optInTimePeriod: params.optInTimePeriod, + optInNbDev: params.optInNbDev + }); + } +} + +methods.stoch = { + requires: [ + 'optInFastK_Period', + 'optInSlowK_Period', + 'optInSlowK_MAType', + 'optInSlowD_Period', + 'optInSlowD_MAType' + ], + create: (params) => { + verifyParams('stoch', params); + + return (data, callback) => execute(callback, { + name: "STOCH", + high: data.high, + low: data.low, + close: data.close, + startIdx: 0, + endIdx: data.close.length - 1, + + optInFastK_Period: params.optInFastK_Period, + optInSlowK_Period: params.optInSlowK_Period, + optInSlowK_MAType: params.optInSlowK_MAType, + optInSlowD_Period: params.optInSlowD_Period, + optInSlowD_MAType: params.optInSlowD_MAType + }); + } +} + + +methods.stochf = { + requires: [ + 'optInFastK_Period', + 'optInFastD_Period', + 'optInFastD_MAType' + ], + create: (params) => { + verifyParams('stochf', params); + + return (data, callback) => execute(callback, { + name: "STOCHF", + high: data.high, + low: data.low, + close: data.close, + startIdx: 0, + endIdx: data.high.length - 1, + + optInFastK_Period: params.optInFastK_Period, + optInFastD_Period: params.optInFastD_Period, + optInFastD_MAType: params.optInFastD_MAType + }); + } +} + +methods.stochrsi = { + requires: [ + 'optInTimePeriod', + 'optInFastK_Period', + 'optInFastD_Period', + 'optInFastD_MAType' + ], + create: (params) => { + verifyParams('stochrsi', params); + + return (data, callback) => execute(callback, { + name: "STOCHRSI", + inReal: data.close, + startIdx: 0, + endIdx: data.close.length - 1, + + optInTimePeriod: params.optInTimePeriod, + optInFastK_Period: params.optInFastK_Period, + optInFastD_Period: params.optInFastD_Period, + optInFastD_MAType: params.optInFastD_MAType + }); + } +} + +methods.t3 = { + requires: [ + 'optInTimePeriod', + 'optInFastK_Period', + 'optInFastD_Period', + 'optInFastD_MAType' + ], + create: (params) => { + verifyParams('t3', params); + + return (data, callback) => execute(callback, { + name: "T3", + inReal: data.close, + startIdx: 0, + endIdx: data.close.length - 1, + optInTimePeriod: params.optInTimePeriod, + optInVFactor: params.optInVFactor + }); + } +} + +methods.tema = { + requires: ['optInTimePeriod'], + create: (params) => { + verifyParams('tema', params); + + return (data, callback) => execute(callback, { + name: "TEMA", + inReal: data.close, + startIdx: 0, + endIdx: data.close.length - 1, + optInTimePeriod: params.optInTimePeriod + }); + } +} + +methods.trange = { + requires: ['optInTimePeriod'], + create: (params) => { + verifyParams('trange', params); + + return (data, callback) => execute(callback, { + name: "TRANGE", + high: data.high, + low: data.low, + close: data.close, + startIdx: 0, + endIdx: data.high.length - 1, + optInTimePeriod: params.optInTimePeriod + }); + } +} + +methods.trima = { + requires: ['optInTimePeriod'], + create: (params) => { + verifyParams('trima', params); + + return (data, callback) => execute(callback, { + name: "TRIMA", + inReal: data.close, + startIdx: 0, + endIdx: data.close.length - 1, + optInTimePeriod: params.optInTimePeriod + }); + } +} + +methods.trix = { + requires: ['optInTimePeriod'], + create: (params) => { + verifyParams('trix', params); + + return (data, callback) => execute(callback, { + name: "TRIX", + inReal: data.close, + startIdx: 0, + endIdx: data.close.length - 1, + optInTimePeriod: params.optInTimePeriod + }); + } +} + +methods.tsf = { + requires: ['optInTimePeriod'], + create: (params) => { + verifyParams('tsf', params); + + return (data, callback) => execute(callback, { + name: "TSF", + inReal: data.close, + startIdx: 0, + endIdx: data.close.length - 1, + optInTimePeriod: params.optInTimePeriod + }); + } +} + +methods.typprice = { + requires: ['optInTimePeriod'], + create: (params) => { + verifyParams('typprice', params); + + return (data, callback) => execute(callback, { + name: "TYPPRICE", + high: data.high, + low: data.low, + close: data.close, + startIdx: 0, + endIdx: data.high.length - 1, + optInTimePeriod: params.optInTimePeriod + }); + } +} + +methods.ultosc = { + requires: ['optInTimePeriod1', 'optInTimePeriod2', 'optInTimePeriod3'], + create: (params) => { + verifyParams('ultosc', params); + + return (data, callback) => execute(callback, { + name: "ULTOSC", + high: data.high, + low: data.low, + close: data.close, + startIdx: 0, + endIdx: data.high.length - 1, + optInTimePeriod1: params.optInTimePeriod1, + optInTimePeriod2: params.optInTimePeriod2, + optInTimePeriod3: params.optInTimePeriod3 + }); + } +} + +methods.variance = { + requires: ['optInTimePeriod', 'optInNbDev'], + create: (params) => { + verifyParams('variance', params); + + return (data, callback) => execute(callback, { + name: "VAR", + inReal: data.close, + startIdx: 0, + endIdx: data.close.length - 1, + optInTimePeriod: params.optInTimePeriod, + optInNbDev: params.optInTimePeriod + }); + } +} + +methods.wclprice = { + requires: ['optInTimePeriod'], + create: (params) => { + verifyParams('wclprice', params); + + return (data, callback) => execute(callback, { + name: "WCLPRICE", + high: data.high, + low: data.low, + close: data.close, + startIdx: 0, + endIdx: data.high.length - 1, + optInTimePeriod: params.optInTimePeriod + }); + } +} + +methods.willr = { + requires: ['optInTimePeriod'], + create: (params) => { + verifyParams('willr', params); + + return (data, callback) => execute(callback, { + name: "WILLR", + high: data.high, + low: data.low, + close: data.close, + startIdx: 0, + endIdx: data.high.length - 1, + optInTimePeriod: params.optInTimePeriod + }); + } +} + +methods.wma = { + requires: ['optInTimePeriod'], + create: (params) => { + verifyParams('wma', params); + + return (data, callback) => execute(callback, { + name: "WMA", + inReal: data.close, + startIdx: 0, + endIdx: data.close.length - 1, + optInTimePeriod: params.optInTimePeriod + }); + } +} + +module.exports = methods; + diff --git a/core/tools/candleLoader.js b/core/tools/candleLoader.js new file mode 100644 index 0000000..f39bf62 --- /dev/null +++ b/core/tools/candleLoader.js @@ -0,0 +1,100 @@ +// TODO: properly handle a daterange for which no data is available. + +const batchSize = 1000; + +const _ = require('lodash'); +const fs = require('fs'); +const moment = require('moment'); + +const util = require('../../core/util'); +const config = util.getConfig(); +const dirs = util.dirs(); +const log = require(dirs.core + '/log'); + +const adapter = config[config.adapter]; +const Reader = require(dirs.gekko + adapter.path + '/reader'); +const daterange = config.daterange; + +const CandleBatcher = require(dirs.core + 'candleBatcher'); + +const to = moment.utc(daterange.to).startOf('minute'); +const from = moment.utc(daterange.from).startOf('minute'); +const toUnix = to.unix(); + +if(to <= from) + util.die('This daterange does not make sense.') + +if(!from.isValid()) + util.die('invalid `from`'); + +if(!to.isValid()) + util.die('invalid `to`'); + +let iterator = { + from: from.clone(), + to: from.clone().add(batchSize, 'm').subtract(1, 's') +} + +var DONE = false; + +var result = []; +var reader = new Reader(); +var batcher; +var next; +var doneFn = () => { + process.nextTick(() => { + next(result); + }) +}; + +module.exports = function(candleSize, _next) { + next = _.once(_next); + + batcher = new CandleBatcher(candleSize) + .on('candle', handleBatchedCandles); + + getBatch(); +} + +const getBatch = () => { + reader.get( + iterator.from.unix(), + iterator.to.unix(), + 'full', + handleCandles + ) +} + +const shiftIterator = () => { + iterator = { + from: iterator.from.clone().add(batchSize, 'm'), + to: iterator.from.clone().add(batchSize * 2, 'm').subtract(1, 's') + } +} + +const handleCandles = (err, data) => { + if(err) { + console.error(err); + util.die('Encountered an error..') + } + + if(_.size(data) && _.last(data).start >= toUnix || iterator.from.unix() >= toUnix) + DONE = true; + + batcher.write(data); + batcher.flush(); + + if(DONE) { + reader.close(); + + setTimeout(doneFn, 100); + + } else { + shiftIterator(); + getBatch(); + } +} + +const handleBatchedCandles = candle => { + result.push(candle); +} diff --git a/core/tools/configBuilder.js b/core/tools/configBuilder.js new file mode 100644 index 0000000..7f40348 --- /dev/null +++ b/core/tools/configBuilder.js @@ -0,0 +1,42 @@ +const fs = require('fs'); +const _ = require('lodash'); +const toml = require('toml'); + +const util = require('../util'); +const dirs = util.dirs(); + +const getTOML = function(fileName) { + var raw = fs.readFileSync(fileName); + return toml.parse(raw); +} + +// build a config object out of a directory of TOML files +module.exports = function() { + const configDir = util.dirs().config; + + let _config = getTOML(configDir + 'general.toml'); + fs.readdirSync(configDir + 'plugins').forEach(function(pluginFile) { + let pluginName = _.first(pluginFile.split('.')) + _config[pluginName] = getTOML(configDir + 'plugins/' + pluginFile); + }); + + // attach the proper adapter + let adapter = _config.adapter; + _config[adapter] = getTOML(configDir + 'adapters/' + adapter + '.toml'); + + if(_config.tradingAdvisor.enabled) { + // also load the strat + let strat = _config.tradingAdvisor.method; + let stratFile = configDir + 'strategies/' + strat + '.toml'; + if(!fs.existsSync(stratFile)) + util.die('Cannot find the strategy config file for ' + strat); + _config[strat] = getTOML(stratFile); + } + + const mode = util.gekkoMode(); + + if(mode === 'backtest') + _config.backtest = getTOML(configDir + 'backtest.toml'); + + return _config; +} \ No newline at end of file diff --git a/core/tools/dataStitcher.js b/core/tools/dataStitcher.js new file mode 100644 index 0000000..252ef93 --- /dev/null +++ b/core/tools/dataStitcher.js @@ -0,0 +1,233 @@ +var _ = require('lodash'); +var fs = require('fs'); +var moment = require('moment'); + +var util = require('../util'); +var config = util.getConfig(); +var dirs = util.dirs(); +var log = require(dirs.core + '/log'); + +var Stitcher = function(batcher) { + this.batcher = batcher; +} + +Stitcher.prototype.ago = function(ts) { + var now = moment().utc(); + var then = moment.unix(ts).utc(); + return now.diff(then, 'minutes') + ' minutes ago'; +} + +Stitcher.prototype.verifyExchange = function() { + require(dirs.gekko + 'exchange/dependencyCheck'); + const exchangeChecker = require(dirs.gekko + 'exchange/exchangeChecker'); + const slug = config.watch.exchange.toLowerCase(); + let exchange; + try { + exchange = exchangeChecker.getExchangeCapabilities(slug); + } catch(e) { + util.die(e.message); + } + + if(!exchange) + util.die(`Unsupported exchange: ${slug}`); + + var error = exchangeChecker.cantMonitor(config.watch); + if(error) + util.die(error, true); +} + +Stitcher.prototype.prepareHistoricalData = function(done) { + this.verifyExchange(); + + // - step 1: check most recent stored candle window + // - step 2: check oldest trade reachable by API + // - step 3: see if overlap + // - step 4: feed candle stream into CandleBatcher + + if(config.tradingAdvisor.historySize === 0) + return done(); + + var requiredHistory = config.tradingAdvisor.candleSize * config.tradingAdvisor.historySize; + var Reader = require(dirs.plugins + config.adapter + '/reader'); + + this.reader = new Reader; + + log.info( + '\tThe trading method requests', + requiredHistory, + 'minutes of historic data. Checking availablity..' + ); + + var endTime = moment().utc().startOf('minute'); + var idealStartTime = endTime.clone().subtract(requiredHistory, 'm'); + + this.reader.mostRecentWindow(idealStartTime, endTime, function(localData) { + // now we know what data is locally available, what + // data would we need from the exchange? + + if(!localData) { + log.info('\tNo usable local data available, trying to get as much as possible from the exchange..'); + var idealExchangeStartTime = idealStartTime.clone(); + var idealExchangeStartTimeTS = idealExchangeStartTime.unix(); + } + else if (idealStartTime.unix() < localData.from) { + log.info('\tLocal data is still too recent, trying to get as much as possible from the exchange'); + var idealExchangeStartTime = idealStartTime.clone(); + var idealExchangeStartTimeTS = idealExchangeStartTime.unix(); + } + else { + log.debug('\tAvailable local data:'); + log.debug('\t\tfrom:', this.ago(localData.from)); + log.debug('\t\tto:', this.ago(localData.to)); + + log.info('\tUsable local data available, trying to match with exchange data..') + // local data is available, we need the next minute + + + // make sure we grab back in history far enough + var secondsOverlap = 60 * 15; // 15 minutes + var idealExchangeStartTimeTS = localData.to - secondsOverlap; + var idealExchangeStartTime = moment.unix(idealExchangeStartTimeTS).utc(); + + // already set the + util.setConfigProperty( + 'tradingAdvisor', + 'firstFetchSince', + idealExchangeStartTimeTS + ); + } + + // Limit the history Gekko can try to get from the exchange. + var minutesAgo = endTime.diff(idealExchangeStartTime, 'minutes'); + var maxMinutesAgo = 4 * 60; // 4 hours + if(minutesAgo > maxMinutesAgo) { + log.info('\tPreventing Gekko from requesting', minutesAgo, 'minutes of history.'); + idealExchangeStartTime = endTime.clone().subtract(maxMinutesAgo, 'minutes'); + idealExchangeStartTimeTS = idealExchangeStartTime.unix(); + } + + log.debug('\tFetching exchange data since', this.ago(idealExchangeStartTimeTS)) + this.checkExchangeTrades(idealExchangeStartTime, function(err, exchangeData) { + log.debug('\tAvailable exchange data:'); + log.debug('\t\tfrom:', this.ago(exchangeData.from)); + log.debug('\t\tto:', this.ago(exchangeData.to)); + + // in case we have limited local data, and the + // exchange would offer more than we have: ignore + // the local data.. + if( + localData && + exchangeData && + exchangeData.from < localData.from + ) { + log.debug('\tExchange offered more data than locally available. Ignoring local data'); + localData = false; + } + + var stitchable = localData && exchangeData.from <= localData.to; + if(stitchable) { + log.debug('\tStitching datasets'); + + // we can combine local data with exchange data + if(idealStartTime.unix() >= localData.from) { + log.info( + '\tFull history locally available.', + 'Seeding the trading method with all required historical candles.' + ); + + } else { + + // stitchable but not enough + + log.info( + '\tPartial history locally available, but', + Math.round((localData.from - idealStartTime.unix()) / 60), + 'minutes are missing.') + log.info('\tSeeding the trading method with', + 'partial historical data (Gekko needs more time before', + 'it can give advice).' + ); + } + + // seed all historic data up to the point the exchange can provide. + var from = localData.from; + var to = moment.unix(exchangeData.from).utc() + .startOf('minute') + .subtract(1, 'minute') + .unix(); + + log.debug('\tSeeding with:'); + log.debug('\t\tfrom:', this.ago(from)); + log.debug('\t\tto:', this.ago(to)); + return this.seedLocalData(from, to, done); + + } else if(!stitchable) { + log.debug('\tUnable to stitch datasets.') + // we cannot use any local data.. + log.info( + '\tNot seeding locally available data to the trading method.' + ); + + if(exchangeData.from < idealExchangeStartTimeTS) { + log.info('\tHowever the exchange returned enough data anyway!'); + } else if(localData) { + log.info( + '\tThe exchange does not return enough data.', + Math.round((localData.from - idealStartTime.unix()) / 60), + 'minutes are still missing.' + ); + } + } + + done(); + + }.bind(this)); + }.bind(this)); +} + +Stitcher.prototype.checkExchangeTrades = function(since, next) { + var provider = config.watch.exchange.toLowerCase(); + var DataProvider = require(util.dirs().gekko + 'exchange/wrappers/' + provider); + + var exchangeConfig = config.watch; + + // include trader config if trading is enabled + if (_.isObject(config.trader) && config.trader.enabled) { + exchangeConfig = _.extend(config.watch, config.trader); + } + + var watcher = new DataProvider(exchangeConfig); + watcher.getTrades(since, function(e, d) { + if(e) { + util.die(e.message); + } + + if(_.isEmpty(d)) + return util.die( + `Gekko tried to retrieve data since ${since.format('YYYY-MM-DD HH:mm:ss')}, however + ${provider} did not return any trades.` + ); + + next(e, { + from: _.first(d).date, + to: _.last(d).date + }) + }); +} + +Stitcher.prototype.seedLocalData = function(from, to, next) { + this.reader.get(from, to, 'full', function(err, rows) { + rows = _.map(rows, row => { + row.start = moment.unix(row.start); + return row; + }); + + this.batcher.write(rows); + this.batcher.flush(); + this.reader.close(); + next(); + + }.bind(this)); +} + +module.exports = Stitcher; diff --git a/core/tools/dateRangeScanner.js b/core/tools/dateRangeScanner.js new file mode 100644 index 0000000..4990dc7 --- /dev/null +++ b/core/tools/dateRangeScanner.js @@ -0,0 +1,138 @@ +var BATCH_SIZE = 60; // minutes +var MISSING_CANDLES_ALLOWED = 3; // minutes, per batch + +var _ = require('lodash'); +var moment = require('moment'); +var async = require('async'); + +var util = require('../util'); +var config = util.getConfig(); +var dirs = util.dirs(); +var log = require(dirs.core + 'log'); + +var adapter = config[config.adapter]; +var Reader = require(dirs.gekko + adapter.path + '/reader'); + +var reader = new Reader(); + +// todo: rewrite with generators or async/await.. +var scan = function(done) { + log.info('Scanning local history for backtestable dateranges.'); + + reader.tableExists('candles', (err, exists) => { + + if(err) + return done(err, null, reader); + + if(!exists) + return done(null, [], reader); + + async.parallel({ + boundry: reader.getBoundry, + available: reader.countTotal + }, (err, res) => { + + var first = res.boundry.first; + var last = res.boundry.last; + + var optimal = (last - first) / 60; + + log.debug('Available', res.available); + log.debug('Optimal', optimal); + + // There is a candle for every minute + if(res.available === optimal + 1) { + log.info('Gekko is able to fully use the local history.'); + return done(false, [{ + from: first, + to: last + }], reader); + } + + // figure out where the gaps are.. + + var missing = optimal - res.available + 1; + + log.info(`The database has ${missing} candles missing, Figuring out which ones...`); + + var iterator = { + from: last - (BATCH_SIZE * 60), + to: last + } + + var batches = []; + + // loop through all candles we have + // in batches and track whether they + // are complete + async.whilst( + () => { + return iterator.from > first + }, + next => { + var from = iterator.from; + var to = iterator.to; + reader.count( + from, + iterator.to, + (err, count) => { + var complete = count + MISSING_CANDLES_ALLOWED > BATCH_SIZE; + + if(complete) + batches.push({ + to: to, + from: from + }); + + next(); + } + ); + + iterator.from -= BATCH_SIZE * 60; + iterator.to -= BATCH_SIZE * 60; + }, + () => { + if(batches.length === 0) { + return done(null, [], reader); + } + + // batches is now a list like + // [ {from: unix, to: unix } ] + + var ranges = [ batches.shift() ]; + + _.each(batches, batch => { + var curRange = _.last(ranges); + if(batch.to === curRange.from) + curRange.from = batch.from; + else + ranges.push( batch ); + }) + + // we have been counting chronologically reversed + // (backwards, from now into the past), flip definitions + ranges = ranges.reverse(); + + _.map(ranges, r => { + return { + from: r.to, + to: r.from + } + }); + + + // ranges is now a list like + // [ {from: unix, to: unix } ] + // + // it contains all valid dataranges available for the + // end user. + + return done(false, ranges, reader); + } + ) + }); + + }); +} + +module.exports = scan; \ No newline at end of file diff --git a/core/tulind.js b/core/tulind.js new file mode 100644 index 0000000..bd40a1c --- /dev/null +++ b/core/tulind.js @@ -0,0 +1,1002 @@ +var semver = require("semver"); +var _ = require('lodash'); + +// validate that talib is installed, if not we'll throw an exception which will +// prevent further loading or out outside this module +try { + var tulind = require("tulind"); +} catch (e) { + module.exports = null; + return; +} + +var tulindError = 'Gekko was unable to configure Tulip Indicators:\n\t'; + +// Wrapper that executes a tulip indicator +var execute = function(callback, params) { + var tulindCallback = function(err, result) { + if (err) return callback(err); + var table = {} + for (var i = 0; i < params.results.length; ++i) { + table[params.results[i]] = result[i]; + } + callback(null, table); + }; + + return params.indicator.indicator(params.inputs, params.options, tulindCallback); +} + +// Helper that makes sure all required parameters +// for a specific talib indicator are present. +var verifyParams = (methodName, params) => { + var requiredParams = methods[methodName].requires; + + _.each(requiredParams, paramName => { + if(!_.has(params, paramName)) { + throw new Error(tulindError + methodName + ' requires ' + paramName + '.'); + } + + var val = params[paramName]; + + if(!_.isNumber(val)) { + throw new Error(tulindError + paramName + ' needs to be a number'); + } + }); +} + +var methods = {}; + +methods.ad = { + requires: [], + create: (params) => { + verifyParams('ad', params); + + return (data, callback) => execute(callback, { + indicator: tulind.indicators.ad, + inputs: [data.high, data.low, data.close, data.volume], + options: [], + results: ['result'], + }); + } +} + +methods.adosc = { + requires: ['optInFastPeriod', 'optInSlowPeriod'], + create: (params) => { + verifyParams('adosc', params); + + return (data, callback) => execute(callback, { + indicator: tulind.indicators.adosc, + inputs: [data.high, data.low, data.close, data.volume], + options: [params.optInFastPeriod, params.optInSlowPeriod], + results: ['result'], + }); + } +} + +methods.adx = { + requires: ['optInTimePeriod'], + create: (params) => { + verifyParams('adx', params); + + return (data, callback) => execute(callback, { + indicator: tulind.indicators.adx, + inputs: [data.high, data.low, data.close], + options: [params.optInTimePeriod], + results: ['result'], + }); + } +} + +methods.adxr = { + requires: ['optInTimePeriod'], + create: (params) => { + verifyParams('adxr', params); + + return (data, callback) => execute(callback, { + indicator: tulind.indicators.adxr, + inputs: [data.high, data.low, data.close], + options: [params.optInTimePeriod], + results: ['result'], + }); + } +} + +methods.ao = { + requires: [], + create: (params) => { + verifyParams('ao', params); + + return (data, callback) => execute(callback, { + indicator: tulind.indicators.ao, + inputs: [data.high, data.low], + options: [], + results: ['result'], + }); + } +} + +methods.apo = { + requires: ['optInFastPeriod', 'optInSlowPeriod'], + create: (params) => { + verifyParams('apo', params); + + return (data, callback) => execute(callback, { + indicator: tulind.indicators.apo, + inputs: [data.close], + options: [params.optInFastPeriod, params.optInSlowPeriod], + results: ['result'], + }); + } +} + +methods.aroon = { + requires: ['optInTimePeriod'], + create: (params) => { + verifyParams('aroon', params); + + return (data, callback) => execute(callback, { + indicator: tulind.indicators.aroon, + inputs: [data.high, data.low], + options: [params.optInTimePeriod], + results: ['aroonDown', 'aroonUp'], + }); + } +} + +methods.aroonosc = { + requires: ['optInTimePeriod'], + create: (params) => { + verifyParams('aroonosc', params); + + return (data, callback) => execute(callback, { + indicator: tulind.indicators.aroonosc, + inputs: [data.high, data.low], + options: [params.optInTimePeriod], + results: ['result'], + }); + } +} + +methods.atr = { + requires: ['optInTimePeriod'], + create: (params) => { + verifyParams('atr', params); + + return (data, callback) => execute(callback, { + indicator: tulind.indicators.atr, + inputs: [data.high, data.low, data.close], + options: [params.optInTimePeriod], + results: ['result'], + }); + } +} + +methods.avgprice = { + requires: [], + create: (params) => { + verifyParams('avgprice', params); + + return (data, callback) => execute(callback, { + indicator: tulind.indicators.avgprice, + inputs: [data.open, data.high, data.low, data.close], + options: [], + results: ['result'], + }); + } +} + +methods.bbands = { + requires: ['optInTimePeriod', 'optInNbStdDevs'], + create: (params) => { + verifyParams('bbands', params); + + return (data, callback) => execute(callback, { + indicator: tulind.indicators.bbands, + inputs: [data.close], + options: [params.optInTimePeriod, params.optInNbStdDevs], + results: ['bbandsLower', 'bbandsMiddle', 'bbandsUpper'], + }); + } +} + +methods.bop = { + requires: [], + create: (params) => { + verifyParams('bop', params); + + return (data, callback) => execute(callback, { + indicator: tulind.indicators.bop, + inputs: [data.open, data.high, data.low, data.close], + options: [], + results: ['result'], + }); + } +} + +methods.cci = { + requires: ['optInTimePeriod'], + create: (params) => { + verifyParams('cci', params); + + return (data, callback) => execute(callback, { + indicator: tulind.indicators.cci, + inputs: [data.high, data.low, data.close], + options: [params.optInTimePeriod], + results: ['result'], + }); + } +} + +methods.cmo = { + requires: ['optInTimePeriod'], + create: (params) => { + verifyParams('cmo', params); + + return (data, callback) => execute(callback, { + indicator: tulind.indicators.cmo, + inputs: [data.close], + options: [params.optInTimePeriod], + results: ['result'], + }); + } +} + +methods.cvi = { + requires: ['optInTimePeriod'], + create: (params) => { + verifyParams('cvi', params); + + return (data, callback) => execute(callback, { + indicator: tulind.indicators.cvi, + inputs: [data.high, data.low], + options: [params.optInTimePeriod], + results: ['result'], + }); + } +} + +methods.dema = { + requires: ['optInTimePeriod'], + create: (params) => { + verifyParams('dema', params); + + return (data, callback) => execute(callback, { + indicator: tulind.indicators.dema, + inputs: [data.close], + options: [params.optInTimePeriod], + results: ['result'], + }); + } +} + +methods.di = { + requires: ['optInTimePeriod'], + create: (params) => { + verifyParams('di', params); + + return (data, callback) => execute(callback, { + indicator: tulind.indicators.di, + inputs: [data.high, data.low, data.close], + options: [params.optInTimePeriod], + results: ['diPlus', 'diMinus'], + }); + } +} + +methods.dm = { + requires: ['optInTimePeriod'], + create: (params) => { + verifyParams('dm', params); + + return (data, callback) => execute(callback, { + indicator: tulind.indicators.dm, + inputs: [data.high, data.low], + options: [params.optInTimePeriod], + results: ['dmPlus', 'dmLow'], + }); + } +} + +methods.dpo = { + requires: ['optInTimePeriod'], + create: (params) => { + verifyParams('dpo', params); + + return (data, callback) => execute(callback, { + indicator: tulind.indicators.dpo, + inputs: [data.close], + options: [params.optInTimePeriod], + results: ['result'], + }); + } +} + +methods.dx = { + requires: ['optInTimePeriod'], + create: (params) => { + verifyParams('dx', params); + + return (data, callback) => execute(callback, { + indicator: tulind.indicators.dx, + inputs: [data.high, data.low, data.close], + options: [params.optInTimePeriod], + results: ['result'], + }); + } +} + +methods.ema = { + requires: ['optInTimePeriod'], + create: (params) => { + verifyParams('ema', params); + + return (data, callback) => execute(callback, { + indicator: tulind.indicators.ema, + inputs: [data.close], + options: [params.optInTimePeriod], + results: ['result'], + }); + } +} + +methods.emv = { + requires: [], + create: (params) => { + verifyParams('emv', params); + + return (data, callback) => execute(callback, { + indicator: tulind.indicators.emv, + inputs: [data.high, data.low, data.volume], + options: [params.optInTimePeriod], + results: [], + }); + } +} + +methods.fisher = { + requires: ['optInTimePeriod'], + create: (params) => { + verifyParams('fisher', params); + + return (data, callback) => execute(callback, { + indicator: tulind.indicators.fisher, + inputs: [data.high, data.low], + options: [params.optInTimePeriod], + results: ['fisher', 'fisherPeriod'], + }); + } +} + +methods.fosc = { + requires: ['optInTimePeriod'], + create: (params) => { + verifyParams('fosc', params); + + return (data, callback) => execute(callback, { + indicator: tulind.indicators.fosc, + inputs: [data.close], + options: [params.optInTimePeriod], + results: ['result'], + }); + } +} + +methods.hma = { + requires: ['optInTimePeriod'], + create: (params) => { + verifyParams('hma', params); + + return (data, callback) => execute(callback, { + indicator: tulind.indicators.hma, + inputs: [data.close], + options: [params.optInTimePeriod], + results: ['result'], + }); + } +} + +methods.kama = { + requires: ['optInTimePeriod'], + create: (params) => { + verifyParams('kama', params); + + return (data, callback) => execute(callback, { + indicator: tulind.indicators.kama, + inputs: [data.close], + options: [params.optInTimePeriod], + results: ['result'], + }); + } +} + +methods.kvo = { + requires: ['optInFastPeriod', 'optInSlowPeriod'], + create: (params) => { + verifyParams('kvo', params); + + return (data, callback) => execute(callback, { + indicator: tulind.indicators.kvo, + inputs: [data.high, data.low, data.close, data.volume], + options: [params.optInFastPeriod, params.optInSlowPeriod], + results: ['result'], + }); + } +} + +methods.linreg = { + requires: ['optInTimePeriod'], + create: (params) => { + verifyParams('linreg', params); + + return (data, callback) => execute(callback, { + indicator: tulind.indicators.linreg, + inputs: [data.close], + options: [params.optInTimePeriod], + results: ['result'], + }); + } +} + +methods.linregintercept = { + requires: ['optInTimePeriod'], + create: (params) => { + verifyParams('linregintercept', params); + + return (data, callback) => execute(callback, { + indicator: tulind.indicators.linregintercept, + inputs: [data.close], + options: [params.optInTimePeriod], + results: ['result'], + }); + } +} + +methods.linregslope = { + requires: ['optInTimePeriod'], + create: (params) => { + verifyParams('linregslope', params); + + return (data, callback) => execute(callback, { + indicator: tulind.indicators.linregslope, + inputs: [data.close], + options: [params.optInTimePeriod], + results: ['result'], + }); + } +} + +methods.macd = { + requires: ['optInFastPeriod', 'optInSlowPeriod', 'optInSignalPeriod'], + create: (params) => { + verifyParams('macd', params); + + return (data, callback) => execute(callback, { + indicator: tulind.indicators.macd, + inputs: [data.close], + options: [params.optInFastPeriod, params.optInSlowPeriod, params.optInSignalPeriod], + results: ['macd', 'macdSignal', 'macdHistogram'], + }); + } +} + +methods.marketfi = { + requires: [], + create: (params) => { + verifyParams('marketfi', params); + + return (data, callback) => execute(callback, { + indicator: tulind.indicators.marketfi, + inputs: [data.high, data.low, data.volume], + options: [], + results: ['result'], + }); + } +} + +methods.mass = { + requires: ['optInTimePeriod'], + create: (params) => { + verifyParams('mass', params); + + return (data, callback) => execute(callback, { + indicator: tulind.indicators.mass, + inputs: [data.high, data.low], + options: [params.optInTimePeriod], + results: ['result'], + }); + } +} + +methods.medprice = { + requires: [], + create: (params) => { + verifyParams('medprice', params); + + return (data, callback) => execute(callback, { + indicator: tulind.indicators.medprice, + inputs: [data.high, data.low], + options: [], + results: ['result'], + }); + } +} + +methods.mfi = { + requires: ['optInTimePeriod'], + create: (params) => { + verifyParams('mfi', params); + + return (data, callback) => execute(callback, { + indicator: tulind.indicators.mfi, + inputs: [data.high, data.low, data.close, data.volume], + options: [params.optInTimePeriod], + results: ['result'], + }); + } +} + +methods.msw = { + requires: ['optInTimePeriod'], + create: (params) => { + verifyParams('msw', params); + + return (data, callback) => execute(callback, { + indicator: tulind.indicators.msw, + inputs: [data.close], + options: [params.optInTimePeriod], + results: ['mswSine', 'mswLead'], + }); + } +} + +methods.natr = { + requires: ['optInTimePeriod'], + create: (params) => { + verifyParams('natr', params); + + return (data, callback) => execute(callback, { + indicator: tulind.indicators.natr, + inputs: [data.high, data.low, data.close], + options: [params.optInTimePeriod], + results: ['result'], + }); + } +} + +methods.nvi = { + requires: [], + create: (params) => { + verifyParams('nvi', params); + + return (data, callback) => execute(callback, { + indicator: tulind.indicators.nvi, + inputs: [data.close, data.volume], + options: [], + results: ['result'], + }); + } +} + +methods.obv = { + requires: [], + create: (params) => { + verifyParams('obv', params); + + return (data, callback) => execute(callback, { + indicator: tulind.indicators.obv, + inputs: [data.close, data.volume], + options: [], + results: ['result'], + }); + } +} + +methods.ppo = { + requires: ['optInFastPeriod', 'optInSlowPeriod'], + create: (params) => { + verifyParams('ppo', params); + + return (data, callback) => execute(callback, { + indicator: tulind.indicators.ppo, + inputs: [data.close], + options: [params.optInFastPeriod, params.optInSlowPeriod], + results: ['result'], + }); + } +} + +methods.psar = { + requires: ['optInAcceleration', 'optInMaximum'], + create: (params) => { + verifyParams('psar', params); + + return (data, callback) => execute(callback, { + indicator: tulind.indicators.psar, + inputs: [data.high, data.low], + options: [params.optInAcceleration, params.optInMaximum], + results: ['result'], + }); + } +} + +methods.pvi = { + requires: [], + create: (params) => { + verifyParams('pvi', params); + + return (data, callback) => execute(callback, { + indicator: tulind.indicators.pvi, + inputs: [data.close, data.volume], + options: [], + results: ['result'], + }); + } +} + +methods.qstick = { + requires: ['optInTimePeriod'], + create: (params) => { + verifyParams('qstick', params); + + return (data, callback) => execute(callback, { + indicator: tulind.indicators.qstick, + inputs: [data.open, data.close], + options: [params.optInTimePeriod], + results: ['result'], + }); + } +} + +methods.roc = { + requires: ['optInTimePeriod'], + create: (params) => { + verifyParams('roc', params); + + return (data, callback) => execute(callback, { + indicator: tulind.indicators.roc, + inputs: [data.close], + options: [params.optInTimePeriod], + results: ['result'], + }); + } +} + +methods.rocr = { + requires: ['optInTimePeriod'], + create: (params) => { + verifyParams('rocr', params); + + return (data, callback) => execute(callback, { + indicator: tulind.indicators.rocr, + inputs: [data.close], + options: [params.optInTimePeriod], + results: ['result'], + }); + } +} + +methods.rsi = { + requires: ['optInTimePeriod'], + create: (params) => { + verifyParams('rsi', params); + + return (data, callback) => execute(callback, { + indicator: tulind.indicators.rsi, + inputs: [data.close], + options: [params.optInTimePeriod], + results: ['result'], + }); + } +} + +methods.sma = { + requires: ['optInTimePeriod'], + create: (params) => { + verifyParams('sma', params); + + return (data, callback) => execute(callback, { + indicator: tulind.indicators.sma, + inputs: [data.close], + options: [params.optInTimePeriod], + results: ['result'], + }); + } +} + +methods.stddev = { + requires: ['optInTimePeriod'], + create: (params) => { + verifyParams('stddev', params); + + return (data, callback) => execute(callback, { + indicator: tulind.indicators.stddev, + inputs: [data.close], + options: [params.optInTimePeriod], + results: ['result'], + }); + } +} + +methods.stoch = { + requires: ['optInFastKPeriod', 'optInSlowKPeriod', 'optInSlowDPeriod'], + create: (params) => { + verifyParams('stoch', params); + + return (data, callback) => execute(callback, { + indicator: tulind.indicators.stoch, + inputs: [data.high, data.low, data.close], + options: [params.optInFastKPeriod, params.optInSlowKPeriod, params.optInSlowDPeriod], + results: ['stochK', 'stochD'], + }); + } +} + +methods.sum = { + requires: ['optInTimePeriod'], + create: (params) => { + verifyParams('sum', params); + + return (data, callback) => execute(callback, { + indicator: tulind.indicators.sum, + inputs: [data.close], + options: [params.optInTimePeriod], + results: ['result'], + }); + } +} + +methods.tema = { + requires: ['optInTimePeriod'], + create: (params) => { + verifyParams('tema', params); + + return (data, callback) => execute(callback, { + indicator: tulind.indicators.tema, + inputs: [data.close], + options: [params.optInTimePeriod], + results: ['result'], + }); + } +} + +methods.tr = { + requires: [], + create: (params) => { + verifyParams('tr', params); + + return (data, callback) => execute(callback, { + indicator: tulind.indicators.tr, + inputs: [data.high, data.low, data.close], + options: [], + results: ['result'], + }); + } +} + +methods.trima = { + requires: ['optInTimePeriod'], + create: (params) => { + verifyParams('trima', params); + + return (data, callback) => execute(callback, { + indicator: tulind.indicators.trima, + inputs: [data.close], + options: [params.optInTimePeriod], + results: ['result'], + }); + } +} + +methods.trix = { + requires: ['optInTimePeriod'], + create: (params) => { + verifyParams('trix', params); + + return (data, callback) => execute(callback, { + indicator: tulind.indicators.trix, + inputs: [data.close], + options: [params.optInTimePeriod], + results: ['result'], + }); + } +} + +methods.tsf = { + requires: ['optInTimePeriod'], + create: (params) => { + verifyParams('tsf', params); + + return (data, callback) => execute(callback, { + indicator: tulind.indicators.tsf, + inputs: [data.close], + options: [params.optInTimePeriod], + results: ['result'], + }); + } +} + +methods.typprice = { + requires: [], + create: (params) => { + verifyParams('typprice', params); + + return (data, callback) => execute(callback, { + indicator: tulind.indicators.typprice, + inputs: [data.high, data.low, data.close], + options: [], + results: ['result'], + }); + } +} + +methods.ultosc = { + requires: ['optInTimePeriod1', 'optInTimePeriod2', 'optInTimePeriod3'], + create: (params) => { + verifyParams('ultosc', params); + + return (data, callback) => execute(callback, { + indicator: tulind.indicators.ultosc, + inputs: [data.high, data.low, data.close], + options: [params.optInTimePeriod1, params.optInTimePeriod2, params.optInTimePeriod3], + results: ['result'], + }); + } +} + +methods.vhf = { + requires: ['optInTimePeriod'], + create: (params) => { + verifyParams('vhf', params); + + return (data, callback) => execute(callback, { + indicator: tulind.indicators.vhf, + inputs: [data.close], + options: [params.optInTimePeriod], + results: ['result'], + }); + } +} + +methods.vidya = { + requires: ['optInFastPeriod', 'optInSlowPeriod', 'optInAlpha'], + create: (params) => { + verifyParams('vidya', params); + + return (data, callback) => execute(callback, { + indicator: tulind.indicators.vidya, + inputs: [data.close], + options: [params.optInFastPeriod, params.optInSlowPeriod, params.optInAlpha], + results: ['result'], + }); + } +} + +methods.volatility = { + requires: ['optInTimePeriod'], + create: (params) => { + verifyParams('volatility', params); + + return (data, callback) => execute(callback, { + indicator: tulind.indicators.volatility, + inputs: [data.close], + options: [params.optInTimePeriod], + results: ['result'], + }); + } +} + +methods.vosc = { + requires: ['optInFastPeriod', 'optInSlowPeriod'], + create: (params) => { + verifyParams('vosc', params); + + return (data, callback) => execute(callback, { + indicator: tulind.indicators.vosc, + inputs: [data.volume], + options: [params.optInFastPeriod, params.optInSlowPeriod], + results: ['result'], + }); + } +} + +methods.vwma = { + requires: ['optInTimePeriod'], + create: (params) => { + verifyParams('vwma', params); + + return (data, callback) => execute(callback, { + indicator: tulind.indicators.vwma, + inputs: [data.close, data.volume], + options: [params.optInTimePeriod], + results: ['result'], + }); + } +} + +methods.wad = { + requires: [], + create: (params) => { + verifyParams('wad', params); + + return (data, callback) => execute(callback, { + indicator: tulind.indicators.wad, + inputs: [data.high, data.low, data.close], + options: [], + results: ['result'], + }); + } +} + +methods.wcprice = { + requires: [], + create: (params) => { + verifyParams('wcprice', params); + + return (data, callback) => execute(callback, { + indicator: tulind.indicators.wcprice, + inputs: [data.high, data.low, data.close], + options: [], + results: ['result'], + }); + } +} + +methods.wilders = { + requires: ['optInTimePeriod'], + create: (params) => { + verifyParams('wilders', params); + + return (data, callback) => execute(callback, { + indicator: tulind.indicators.wilders, + inputs: [data.close], + options: [params.optInTimePeriod], + results: ['result'], + }); + } +} + +methods.willr = { + requires: ['optInTimePeriod'], + create: (params) => { + verifyParams('willr', params); + + return (data, callback) => execute(callback, { + indicator: tulind.indicators.willr, + inputs: [data.high, data.low, data.close], + options: [params.optInTimePeriod], + results: ['result'], + }); + } +} + +methods.wma = { + requires: ['optInTimePeriod'], + create: (params) => { + verifyParams('wma', params); + + return (data, callback) => execute(callback, { + indicator: tulind.indicators.wma, + inputs: [data.close], + options: [params.optInTimePeriod], + results: ['result'], + }); + } +} + +methods.zlema = { + requires: ['optInTimePeriod'], + create: (params) => { + verifyParams('zlema', params); + + return (data, callback) => execute(callback, { + indicator: tulind.indicators.zlema, + inputs: [data.close], + options: [params.optInTimePeriod], + results: ['result'], + }); + } +} + +module.exports = methods; + diff --git a/core/util.js b/core/util.js new file mode 100644 index 0000000..7d2cccd --- /dev/null +++ b/core/util.js @@ -0,0 +1,189 @@ +var moment = require('moment'); +var _ = require('lodash'); +var path = require('path'); +var fs = require('fs'); +var semver = require('semver'); +var program = require('commander'); + +var startTime = moment(); + +var _config = false; +var _package = false; +var _nodeVersion = false; +var _gekkoMode = false; +var _gekkoEnv = false; + +var _args = false; + +// helper functions +var util = { + getConfig: function() { + // cache + if(_config) + return _config; + + if(!program.config) + util.die('Please specify a config file.', true); + + if(!fs.existsSync(util.dirs().gekko + program.config)) + util.die('Cannot find the specified config file.', true); + + _config = require(util.dirs().gekko + program.config); + return _config; + }, + // overwrite the whole config + setConfig: function(config) { + _config = config; + }, + setConfigProperty: function(parent, key, value) { + if(parent) + _config[parent][key] = value; + else + _config[key] = value; + }, + getVersion: function() { + return util.getPackage().version; + }, + getPackage: function() { + if(_package) + return _package; + + + _package = JSON.parse( fs.readFileSync(__dirname + '/../package.json', 'utf8') ); + return _package; + }, + getRequiredNodeVersion: function() { + return util.getPackage().engines.node; + }, + recentNode: function() { + var required = util.getRequiredNodeVersion(); + return semver.satisfies(process.version, required); + }, + // check if two moments are corresponding + // to the same time + equals: function(a, b) { + return !(a < b || a > b) + }, + minToMs: function(min) { + return min * 60 * 1000; + }, + defer: function(fn) { + return function(args) { + var args = _.toArray(arguments); + return _.defer(function() { fn.apply(this, args) }); + } + }, + logVersion: function() { + return `Gekko version: v${util.getVersion()}` + + `\nNodejs version: ${process.version}`; + }, + die: function(m, soft) { + + if(_gekkoEnv === 'child-process') { + return process.send({type: 'error', error: '\n ERROR: ' + m + '\n'}); + } + + var log = console.log.bind(console); + + if(m) { + if(soft) { + log('\n ERROR: ' + m + '\n\n'); + } else { + log(`\nGekko encountered an error and can\'t continue`); + log('\nError:\n'); + log(m, '\n\n'); + log('\nMeta debug info:\n'); + log(util.logVersion()); + log(''); + } + } + process.exit(1); + }, + dirs: function() { + var ROOT = __dirname + '/../'; + + return { + gekko: ROOT, + core: ROOT + 'core/', + markets: ROOT + 'core/markets/', + exchanges: ROOT + 'exchange/wrappers/', + plugins: ROOT + 'plugins/', + methods: ROOT + 'strategies/', + indicators: ROOT + 'strategies/indicators/', + budfox: ROOT + 'core/budfox/', + importers: ROOT + 'importers/exchanges/', + tools: ROOT + 'core/tools/', + workers: ROOT + 'core/workers/', + web: ROOT + 'web/', + config: ROOT + 'config/', + broker: ROOT + 'exchange/' + } + }, + inherit: function(dest, source) { + require('util').inherits( + dest, + source + ); + }, + makeEventEmitter: function(dest) { + util.inherit(dest, require('events').EventEmitter); + }, + setGekkoMode: function(mode) { + _gekkoMode = mode; + }, + gekkoMode: function() { + if(_gekkoMode) + return _gekkoMode; + + if(program['import']) + return 'importer'; + else if(program.backtest) + return 'backtest'; + else + return 'realtime'; + }, + gekkoModes: function() { + return [ + 'importer', + 'backtest', + 'realtime' + ] + }, + setGekkoEnv: function(env) { + _gekkoEnv = env; + }, + gekkoEnv: function() { + return _gekkoEnv || 'standalone'; + }, + launchUI: function() { + if(program['ui']) + return true; + else + return false; + }, + getStartTime: function() { + return startTime; + }, +} + +// NOTE: those options are only used +// in stand alone mode +program + .version(util.logVersion()) + .option('-c, --config ', 'Config file') + .option('-b, --backtest', 'backtesting mode') + .option('-i, --import', 'importer mode') + .option('--ui', 'launch a web UI') + .parse(process.argv); + +// make sure the current node version is recent enough +if(!util.recentNode()) + util.die([ + 'Your local version of Node.js is too old. ', + 'You have ', + process.version, + ' and you need atleast ', + util.getRequiredNodeVersion() + ].join(''), true); + +module.exports = util; diff --git a/core/workers/datasetScan/parent.js b/core/workers/datasetScan/parent.js new file mode 100644 index 0000000..c345610 --- /dev/null +++ b/core/workers/datasetScan/parent.js @@ -0,0 +1,54 @@ +var _ = require('lodash'); +var moment = require('moment'); +var async = require('async'); +var os = require('os'); + +var util = require('../../util'); +var dirs = util.dirs(); + +var dateRangeScan = require('../dateRangeScan/parent'); + +module.exports = function(config, done) { + + util.setConfig(config); + + var adapter = config[config.adapter]; + var scan = require(dirs.gekko + adapter.path + '/scanner'); + + scan((err, markets) => { + + if(err) + return done(err); + + let numCPUCores = os.cpus().length; + if(numCPUCores === undefined) + numCPUCores = 1; + async.eachLimit(markets, numCPUCores, (market, next) => { + + let marketConfig = _.clone(config); + marketConfig.watch = market; + + dateRangeScan(marketConfig, (err, ranges) => { + if(err) + return next(); + + market.ranges = ranges; + + next(); + }); + + }, err => { + let resp = { + datasets: [], + errors: [] + } + markets.forEach(market => { + if(market.ranges) + resp.datasets.push(market); + else + resp.errors.push(market); + }) + done(err, resp); + }) + }); +} diff --git a/core/workers/dateRangeScan/child.js b/core/workers/dateRangeScan/child.js new file mode 100644 index 0000000..eb6da96 --- /dev/null +++ b/core/workers/dateRangeScan/child.js @@ -0,0 +1,26 @@ +var util = require(__dirname + '/../../util'); + +var dirs = util.dirs(); +var ipc = require('relieve').IPCEE(process); + +ipc.on('start', config => { + + // force correct gekko env + util.setGekkoEnv('child-process'); + + // force disable debug + config.debug = false; + + // persist config + util.setConfig(config); + + var scan = require(dirs.tools + 'dateRangeScanner'); + scan( + (err, ranges, reader) => { + reader.close(); + ipc.send('ranges', ranges); + process.exit(0); + } + ); +}); + diff --git a/core/workers/dateRangeScan/parent.js b/core/workers/dateRangeScan/parent.js new file mode 100644 index 0000000..6403854 --- /dev/null +++ b/core/workers/dateRangeScan/parent.js @@ -0,0 +1,21 @@ +var ForkTask = require('relieve').tasks.ForkTask; +var fork = require('child_process').fork; + +module.exports = function(config, done) { + var debug = typeof v8debug === 'object'; + if (debug) { + process.execArgv = []; + } + + task = new ForkTask(fork(__dirname + '/child')); + + task.send('start', config); + + task.once('ranges', ranges => { + return done(false, ranges); + }); + task.on('exit', code => { + if(code !== 0) + done('ERROR, unable to scan dateranges, please check the console.'); + }); +} diff --git a/core/workers/loadCandles/child.js b/core/workers/loadCandles/child.js new file mode 100644 index 0000000..c1f7054 --- /dev/null +++ b/core/workers/loadCandles/child.js @@ -0,0 +1,28 @@ +var start = (config, candleSize, daterange) => { + var util = require(__dirname + '/../../util'); + + // force correct gekko env + util.setGekkoEnv('child-process'); + + // force disable debug + config.debug = false; + util.setConfig(config); + + var dirs = util.dirs(); + + var load = require(dirs.tools + 'candleLoader'); + load(config.candleSize, candles => { + process.send(candles); + }) +} + +process.send('ready'); + +process.on('message', (m) => { + if(m.what === 'start') + start(m.config, m.candleSize, m.daterange); +}); + +process.on('disconnect', function() { + process.exit(0); +}) diff --git a/core/workers/loadCandles/parent.js b/core/workers/loadCandles/parent.js new file mode 100644 index 0000000..fa5465c --- /dev/null +++ b/core/workers/loadCandles/parent.js @@ -0,0 +1,68 @@ +// example usage: + +// let config = { +// watch: { +// exchange: 'poloniex', +// currency: 'USDT', +// asset: 'BTC' +// }, +// daterange: { +// from: '2016-05-22 11:22', +// to: '2016-06-03 19:56' +// }, +// adapter: 'sqlite', +// sqlite: { +// path: 'plugins/sqlite', + +// dataDirectory: 'history', +// version: 0.1, + +// dependencies: [{ +// module: 'sqlite3', +// version: '3.1.4' +// }] +// }, +// candleSize: 100 +// } + +// module.exports(config, function(err, data) { +// console.log('FINAL CALLBACK'); +// console.log('err', err); +// console.log('data', data.length); +// }) + + +const fork = require('child_process').fork; +const _ = require('lodash'); + +module.exports = (config, callback) => { + var debug = typeof v8debug === 'object'; + if (debug) { + process.execArgv = []; + } + + const child = fork(__dirname + '/child'); + + const message = { + what: 'start', + config + } + + const done = _.once(callback); + + child.on('message', function(m) { + if(m === 'ready') + return child.send(message); + + // else we are done and have candles! + done(null, m); + if (this.connected) { + this.disconnect(); + } + }); + + child.on('exit', code => { + if(code !== 0) + done('ERROR, unable to load candles, please check the console.'); + }); +} diff --git a/core/workers/pipeline/child.js b/core/workers/pipeline/child.js new file mode 100644 index 0000000..92c471d --- /dev/null +++ b/core/workers/pipeline/child.js @@ -0,0 +1,66 @@ +/* + + Gekko is a Bitcoin trading bot for popular Bitcoin exchanges written + in node, it features multiple trading methods using technical analysis. + + If you are interested in how Gekko works, read more about Gekko's + architecture here: + + https://github.com/askmike/gekko/blob/stable/docs/internals/architecture.md + + Disclaimer: + + USE AT YOUR OWN RISK! + + The author of this project is NOT responsible for any damage or loss caused + by this software. There can be bugs and the bot may not perform as expected + or specified. Please consider testing it first with paper trading and/or + backtesting on historical data. Also look at the code to see what how + it is working. + +*/ + +var start = (mode, config) => { + var util = require(__dirname + '/../../util'); + + // force correct gekko env + util.setGekkoEnv('child-process'); + + var dirs = util.dirs(); + + // force correct gekko mode & config + util.setGekkoMode(mode); + util.setConfig(config); + + var pipeline = require(dirs.core + 'pipeline'); + pipeline({ + config: config, + mode: mode + }); +} + +process.send('ready'); + +process.on('message', function(m) { + if(m.what === 'start') + start(m.mode, m.config); + + if(m.what === 'exit') + process.exit(0); +}); + +process.on('disconnect', function() { + console.log('disconnect'); + process.exit(-1); +}) + +process + .on('unhandledRejection', (message, p) => { + console.error('unhandledRejection', message); + process.send({type: 'error', message: message}); + }) + .on('uncaughtException', err => { + console.error('uncaughtException', err); + process.send({type: 'error', error: err}); + process.exit(1); + }); \ No newline at end of file diff --git a/core/workers/pipeline/messageHandlers/backtestHandler.js b/core/workers/pipeline/messageHandlers/backtestHandler.js new file mode 100644 index 0000000..6ee3afa --- /dev/null +++ b/core/workers/pipeline/messageHandlers/backtestHandler.js @@ -0,0 +1,25 @@ +// Relay the backtest message it when it comes in. + +module.exports = done => { + let backtest; + + return { + message: message => { + if(message.type === 'error') { + done(message.error); + } + + if(message.backtest) { + done(null, message.backtest); + } + }, + exit: status => { + if(status !== 0) { + if(backtest) + console.error('Child process died after finishing backtest'); + else + done('Child process has died.'); + } + } + } +} \ No newline at end of file diff --git a/core/workers/pipeline/messageHandlers/importerHandler.js b/core/workers/pipeline/messageHandlers/importerHandler.js new file mode 100644 index 0000000..7c54287 --- /dev/null +++ b/core/workers/pipeline/messageHandlers/importerHandler.js @@ -0,0 +1,26 @@ +module.exports = cb => { + + return { + message: message => { + + if(message.event === 'marketUpdate') + cb(null, { + done: false, + latest: message.payload + }) + + else if(message.type === 'error') { + cb(message.error); + } + + else if(message.type === 'log') + console.log(message.log); + }, + exit: status => { + if(status !== 0) + return cb('Child process has died.'); + else + cb(null, { done: true }); + } + } +} \ No newline at end of file diff --git a/core/workers/pipeline/messageHandlers/realtimeHandler.js b/core/workers/pipeline/messageHandlers/realtimeHandler.js new file mode 100644 index 0000000..5ada6e6 --- /dev/null +++ b/core/workers/pipeline/messageHandlers/realtimeHandler.js @@ -0,0 +1,24 @@ +// pass back all messages as is +// (except for errors and logs) + +module.exports = cb => { + + return { + message: message => { + + if(message.type === 'error') { + cb(message.error); + } + + else + cb(null, message); + + }, + exit: status => { + if(status !== 0) + cb('Child process has died.'); + else + cb(null, { done: true }); + } + } +} \ No newline at end of file diff --git a/core/workers/pipeline/parent.js b/core/workers/pipeline/parent.js new file mode 100644 index 0000000..d24488a --- /dev/null +++ b/core/workers/pipeline/parent.js @@ -0,0 +1,34 @@ +var fork = require('child_process').fork; + +module.exports = (mode, config, callback) => { + var debug = typeof v8debug === 'object'; + if (debug) { + process.execArgv = []; + } + + var child = fork(__dirname + '/child'); + + // How we should handle client messages depends + // on the mode of the Pipeline that is being ran. + var handle = require('./messageHandlers/' + mode + 'Handler')(callback); + + var message = { + what: 'start', + mode: mode, + config: config + }; + + child.on('message', function(m) { + if(m === 'ready') + return child.send(message); + + if(m === 'done') + return child.send({what: 'exit'}); + + handle.message(m); + }); + + child.on('exit', handle.exit); + + return child; +} diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..a9f069a --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,46 @@ +version: '3' +services: + gekko: + build: ./ + volumes: + - ./volumes/gekko/history:/usr/src/app/history + - ./config.js:/usr/src/app/config.js + links: + - redis +# - postgresql + environment: + - HOST + - PORT + - USE_SSL + ports: # you can comment this out when using the nginx frontend + - "${PORT}:${PORT}" +## optionally set nginx vars if you wish to frontend it with nginx +# environment: +# - VIRTUAL_HOST=gekko +# - PORT=3000 +# - DOMAIN=gekko + + redis: + image: redis:latest + volumes: + - ./volumes/redis:/data +## optionally uncomment if you wish to use nginx as a frontend +# nginx: +# restart: always +# image: jwilder/nginx-proxy +# ports: +# - "80:80" +# volumes: +# - /var/run/docker.sock:/tmp/docker.sock:ro +## optionally uncomment if you wish to use postgresql as a db +# postgresql: +# restart: always +# image: postgres:9.6-alpine +# ports: +# - 5432:5432 +# volumes: +# - ./_postgresql:/var/lib/postgresql/data:rw +# environment: +# POSTGRES_DB: gekko +# POSTGRES_USER: username +# POSTGRES_PASSWORD: password diff --git a/docker-entrypoint.sh b/docker-entrypoint.sh new file mode 100644 index 0000000..81d8aaf --- /dev/null +++ b/docker-entrypoint.sh @@ -0,0 +1,9 @@ +#!/bin/bash + +sed -i 's/127.0.0.1/0.0.0.0/g' /usr/src/app/web/vue/dist/UIconfig.js +sed -i 's/localhost/'${HOST}'/g' /usr/src/app/web/vue/dist/UIconfig.js +sed -i 's/3000/'${PORT}'/g' /usr/src/app/web/vue/dist/UIconfig.js +if [[ "${USE_SSL:-0}" == "1" ]] ; then + sed -i 's/ssl: false/ssl: true/g' /usr/src/app/web/vue/dist/UIconfig.js +fi +exec node gekko "$@" diff --git a/docs/commandline/Importing.md b/docs/commandline/Importing.md new file mode 100644 index 0000000..6e287cc --- /dev/null +++ b/docs/commandline/Importing.md @@ -0,0 +1,41 @@ +# Importing + +*Note: this documentation was written for running Gekko via the command line. If you are using the UI you can simply use the importer under the "local data" tab.* + +If you want to use Gekko to [backtest against historical data](./backtesting.md), you most likely need some historical data to test against. Gekko comes with the functionality to automatically import historical data from some exchanges. However, only a few exchanges support this. You can find out with which exchanges Gekko is able to do this [here](https://github.com/askmike/gekko#supported-exchanges). + +## Setup + +For importing you should [enable and configure](./plugins.md) the following plugin: + + - candleWriter (to store the imported data in a database) + +Besides that, make sure to configure `config.watch` properly. + +## Configure + +In your config set the `importer.daterange` properties to the daterange you would like to import. + +## Run + + node gekko --config config.js --import + +The result will be something like this: + + 2016-06-26 09:12:16 (INFO): Gekko v0.2.2 started + 2016-06-26 09:12:16 (INFO): I'm gonna make you rich, Bud Fox. + + 2016-06-26 09:12:17 (INFO): Setting up Gekko in importer mode + 2016-06-26 09:12:17 (INFO): + 2016-06-26 09:12:17 (INFO): Setting up: + 2016-06-26 09:12:17 (INFO): Candle writer + 2016-06-26 09:12:17 (INFO): Store candles in a database + 2016-06-26 09:12:17 (INFO): + + 2016-06-26 09:12:17 (WARN): The plugin Trading Advisor does not support the mode importer. It has been disabled. + 2016-06-26 09:12:17 (WARN): The plugin Advice logger does not support the mode importer. It has been disabled. + 2016-06-26 09:12:17 (WARN): The plugin Profit Simulator does not support the mode importer. It has been disabled. + 2016-06-26 09:12:18 (DEBUG): Processing 798 new trades. + 2016-06-26 09:12:18 (DEBUG): From 2015-09-09 12:00:04 UTC to 2015-09-09 13:58:55 UTC. (2 hours) + 2016-06-26 09:12:20 (DEBUG): Processing 211 new trades. + (...) diff --git a/docs/commandline/about_the_commandline.md b/docs/commandline/about_the_commandline.md new file mode 100644 index 0000000..7a7664d --- /dev/null +++ b/docs/commandline/about_the_commandline.md @@ -0,0 +1,28 @@ +# About the commandline + +You don't have to use the UI to control Gekko, you can also use the commandline if you don't have access to a window manager or browser (on a server for example, or anything else you want to SSH into). This is only recommended for advanced users who feel comfortable editing config files and running shell commands. + +Using the commandline you run one Gekko instance per command, eg. if you want to import 3 markets you need to run Gekko 3 times. + +## Configuring Gekko + +If you decide you want to run gekko over the commandline you need to decide two things: + +1. What market / settings are you interested in? +2. What should Gekko do? This is either backtest, import or run live. + +For the first one you configure a config file: copy `gekko/sample-config.js` to something else (for example `gekko/config.js`). Configure the plugins to your liking. What plugins you need to enable does depend on the answer of question 2. Check the documentation under commandline for that feature. + +## Running Gekko + +For live mode run: + + node gekko --config config.js + +To backtesting run: + + node gekko --config config.js --backtest + +To import run: + + node gekko --config config.js --import \ No newline at end of file diff --git a/docs/commandline/backtesting.md b/docs/commandline/backtesting.md new file mode 100644 index 0000000..b0dd461 --- /dev/null +++ b/docs/commandline/backtesting.md @@ -0,0 +1,71 @@ +# Backtesting with Gekko + +*Note: this documentation was written for running Gekko via the command line. If you are using the UI you can simply use the importer under the "local data" tab.* + +Gekko is able to backtest strategies against historical data. + +## Setup + +For backtesting you should [enable and configure](./plugins.md) the following plugins: + + - trading advisor (to run your strategy). + - paper trader (to execute simulated trades). + - performance analyzer (to calculate how succesfull the strategy would have been). + +Besides that, make sure to configure `config.watch`. + +## Historical data + +Gekko requires historical data to backtest strategies against. The easiest way to get this is to let Gekko import historical data, however this is not supported by a lot of exchanges (see [here](https://github.com/askmike/gekko#supported-exchanges)). The second easiest and most universal way is to run Gekko on real markets using the UI (or alternatively via the commandline with the plugin sqliteWriter enabled). Though this takes a while, as you need to run Gekko for a week to have a week of data. + +## Configure + +In your config set the `backtest.daterange` to `scan`. This will force Gekko to scan the local database to figure out what dataranges are available. If you already know exactly what daterange you would like to backtest against, you can set the `backtest.daterange` directly (set `backtest.daterange` as an object with the keys `from` and `to` and the values as a data parsable by [moment](http://momentjs.com/docs/#/parsing/)). + +## Run + + node gekko --config config.js --backtest + +The result will be something like this: + + 2016-06-11 08:53:20 (INFO): Gekko v0.2.1 started + 2016-06-11 08:53:20 (INFO): I'm gonna make you rich, Bud Fox. + + 2016-06-11 08:53:20 (INFO): Setting up Gekko in backtest mode + 2016-06-11 08:53:20 (INFO): + 2016-06-11 08:53:20 (WARN): The plugin SQLite Datastore does not support the mode backtest. It has been disabled. + 2016-06-11 08:53:20 (INFO): Setting up: + 2016-06-11 08:53:20 (INFO): Trading Advisor + 2016-06-11 08:53:20 (INFO): Calculate trading advice + 2016-06-11 08:53:20 (INFO): Using the trading method: DEMA + 2016-06-11 08:53:20 (INFO): + + 2016-06-11 08:53:20 (INFO): Setting up: + 2016-06-11 08:53:20 (INFO): Profit Simulator + 2016-06-11 08:53:20 (INFO): Paper trader that logs fake profits. + 2016-06-11 08:53:20 (INFO): + + 2016-06-11 08:58:20 (INFO): Profit simulator got advice to long @ 2016-05-30 04:37:00, buying 1.1880062 BTC + 2016-06-11 08:58:21 (INFO): Profit simulator got advice to short @ 2016-05-31 21:37:00, selling 1.1880062 BTC + 2016-06-11 08:58:21 (INFO): Profit simulator got advice to long @ 2016-06-01 12:37:00, buying 1.14506098 BTC + 2016-06-11 08:58:21 (INFO): Profit simulator got advice to short @ 2016-06-02 14:57:00, selling 1.14506098 BTC + 2016-06-11 08:58:21 (INFO): Profit simulator got advice to long @ 2016-06-02 23:37:00, buying 1.11711818 BTC + 2016-06-11 08:58:21 (INFO): Profit simulator got advice to short @ 2016-06-05 12:57:00, selling 1.11711818 BTC + 2016-06-11 08:58:21 (INFO): Profit simulator got advice to long @ 2016-06-06 02:37:00, buying 1.08456953 BTC + 2016-06-11 08:58:22 (INFO): Profit simulator got advice to short @ 2016-06-07 17:17:00, selling 1.08456953 BTC + 2016-06-11 08:58:22 (INFO): Profit simulator got advice to long @ 2016-06-08 13:17:00, buying 1.05481755 BTC + 2016-06-11 08:58:22 (INFO): Profit simulator got advice to short @ 2016-06-09 14:17:00, selling 1.05481755 BTC + + 2016-06-11 08:53:22 (INFO): (PROFIT REPORT) start time: 2016-05-29 23:34:00 + 2016-06-11 08:53:22 (INFO): (PROFIT REPORT) end time: 2016-06-10 08:56:00 + 2016-06-11 08:53:22 (INFO): (PROFIT REPORT) timespan: 11 days days + + 2016-06-11 08:53:22 (INFO): (PROFIT REPORT) start price: 516.19 + 2016-06-11 08:53:22 (INFO): (PROFIT REPORT) end price: 578.97 + 2016-06-11 08:53:22 (INFO): (PROFIT REPORT) Buy and Hold profit: 12.162189999999995% + + 2016-06-11 08:53:22 (INFO): (PROFIT REPORT) amount of trades: 10 + 2016-06-11 08:53:22 (INFO): (PROFIT REPORT) original simulated balance: 616.19000 USD + 2016-06-11 08:53:22 (INFO): (PROFIT REPORT) current simulated balance: 602.59867 USD + 2016-06-11 08:53:22 (INFO): (PROFIT REPORT) simulated profit: -13.59133 USD (-2.20570%) + 2016-06-11 08:53:22 (INFO): (PROFIT REPORT) simulated yearly profit: -435.53244 USD (-70.68152%) diff --git a/docs/commandline/plugins.md b/docs/commandline/plugins.md new file mode 100644 index 0000000..2d05a29 --- /dev/null +++ b/docs/commandline/plugins.md @@ -0,0 +1,214 @@ +## Enabling plugins + +*Note: this documentation was written for running Gekko via the command line. If you are using the UI you do not have to manually select plugins.* + +Gekko currently has a couple plugins: + +- trading advisor (run a TA strategy against a market) +- trader (execute advice from the TA strategy on a real exchange) +- advice logger +- paper trader +- Mailer +- IRC bot +- 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. + +## Trading Advisor + +If you want Gekko to provide automated trading advice you need to configure this in Gekko. Note that this is a different plugin than the "trader" which is a responsible for actually creating orders based on this advice. (So if you want automated trading you need both this advice as well as the auto trader). + +Documentation about strategies in Gekko can be found [here](../strategies/introduction.md). + +### Trader + +This plugin automatically creates orders based on the advice from the "Trading Advisor" from the market Gekko is watching. This turns Gekko into an automated trading bot. + +Before Gekko can automatically trade you need to create API keys so that Gekko has the rights to create orders on your behalf, the rights Gekko needs are (naming differs per exchange): get info, get balance/portfolio, get open orders, get fee, buy, sell and cancel order. For all exchanges you need the API key and the API secret, for both Bitstamp and CEX.io you also need your username (which is a number at Bitstamp). + +Configure it like this: + + config.trader = { + enabled: true, + key: 'your-api-key', + secret: 'your-api-secret', + username: 'your-username' // your username, only fill in when using bitstamp or cexio + } + +- enabled indicates whether this is on or off. +- key is your API key. +- secret is your API secret. +- username is the username (only required for CEX.io and Bitstamp). + +### Advice logger + +The advice logger is a small plugin that logs new advice calculated by Gekko as soon as there is any. Go to the config and configure it like this: + + config.adviceLogger = { + enabled: true + } + +- enabled indicates whether this is on or off. + +The advice logged advice will look something like this in the terminal: + + 2014-01-15 14:31:44 (INFO): We have new trading advice! + 2014-01-15 14:31:44 (INFO): Position to take: long + 2014-01-15 14:31:44 (INFO): Market price: 5.96 + 2014-01-15 14:31:44 (INFO): Based on market time: 2014-01-15 14:31:01 + +### Paper trader + +The paper trader listens to Gekko's advice and on a sell it will swap all (simulated) currency into (simulated) assets at the current price. On a buy it will be the other way around. + +Go to the config and configure it like this: + + // do you want Gekko to calculate the profit of its own advice? + config.paperTrader = { + enabled: true, + // report the profit in the currency or the asset? + reportInCurrency: true, + // start balance, on what the current balance is compared with + simulationBalance: { + // these are in the unit types configured in the watcher. + asset: 1, + currency: 100 + }, + // only want report after a sell? set to `false`. + verbose: false, + // how much fee in % does each trade cost? + feeMaker: 0.5, + feeTaker: 0.6, + // Using taker or maker fee? + feeUsing: 'maker', + // how much slippage should Gekko assume per trade? + slippage: 0.1 + } + +- enabled indicates whether this is on or off. +- reportInCurrency tells Gekko whether it should report in asset or in the currency. +- simulationBalance tells Gekko with what balance it should start. +- verbose specifies how often Gekko should log the results (false is after every trade, true is after every candle). +- fee is the exchange fee (in %) Gekko should take into consideration when simulating orders. +- slippage is the costs in (in %) associated with not being able to buy / sell at market price.* + +*If you are trading a lot and you are buying 100% currency you might not get it all at market price and you have to walk the book in order to take that position. Also note that Gekko uses the candle close price and is unaware of the top asks bids, also take this into account. It is important that you set this number correctly or the resulted calculated profit be very wrong. Read more information [here](http://www.investopedia.com/terms/s/slippage.asp). Take these into consideration when setting a slippage: + +- How much spread is there normally on this market? +- How thick is the top of the book normally? +- How volatile is this market (the more volatility the bigger the chance you will not get the price you expected)? + +The output will be something like: + + 2013-06-02 18:21:15 (INFO): ADVICE is to SHORT @ 117.465 (0.132) + 2013-06-02 18:21:15 (INFO): (PROFIT REPORT) original balance: 207.465 USD + 2013-06-02 18:21:15 (INFO): (PROFIT REPORT) current balance: 217.465 USD + 2013-06-02 18:21:15 (INFO): (PROFIT REPORT) profit: 10.000 USD (4.820%) + +### Mailer + +Mailer will automatically email you whenever Gekko has a new advice. + + // want Gekko to send a mail on buy or sell advice? + config.mailer = { + enabled: false, // Send Emails if true, false to turn off + sendMailOnStart: true, // Send 'Gekko starting' message if true, not if false + + Email: 'me@gmail.com', // Your GMail address + + // You don't have to set your password here, if you leave it blank we will ask it + // when Gekko's starts. + // + // NOTE: Gekko is an open source project < https://github.com/askmike/gekko >, + // make sure you looked at the code or trust the maintainer of this bot when you + // fill in your email and password. + // + // WARNING: If you have NOT downloaded Gekko from the github page above we CANNOT + // guarantee that your email address & password are safe! + + password: '', // Your GMail Password - if not supplied Gekko will prompt on startup. + tag: '[GEKKO] ' // Prefix all EMail subject lines with this + } + +- enabled indicates whether this is on or off. +- sendMailOnStart will email you right away after Gekko started, you can also use this if you are automatically restarting Gekko to see crash behaviour. +- Email is your email address from which Gekko will send emails (to the same address). +- password is the email password: Gekko needs to login to your account to send emails to you: + + > You don't have to set your password here, if you leave it blank we will ask it + > when Gekko's starts. + > + > NOTE: Gekko is an open source project < https://github.com/askmike/gekko >, + > make sure you looked at the code or trust the maintainer of this bot when you + > fill in your email and password. + > + > WARNING: If you have NOT downloaded Gekko from the github page above we CANNOT + > guarantee that your email address & password are safe! + +- tag is some text that Gekko will put in all subject lines so you can easily group all advices together. + +### IRC bot + +IRC bot is a small plugin that connects Gekko to an IRC channel and lets users interact with it using basic commands. + + config.ircbot = { + enabled: false, + emitUpdates: false, + channel: '#your-channel', + server: 'irc.freenode.net', + botName: 'gekkobot' + } + +- enabled indicates whether this is on or off. +- emitUpdates tells Gekko that whenever there is a new advice it should broadcast this in the channel. +- channel is the IRC channel the bot will connect to. +- server is the IRC server. +- botName is the username Gekko will use. + +### Campfire bot + +Campfire bot is a small plugin that connects Gekko to a Campfire room and lets users interact with it using basic commands. + + config.campfire = { + enabled: false, + emitUpdates: false, + nickname: 'Gordon', + roomId: 673783, + apiKey: 'e3b0c44298fc1c149afbf4c8996', + account: 'your-subdomain' + } + +- enabled indicates whether this is on or off. +- emitUpdates tells Gekko that whenever there is a new advice it should broadcast this in the room. +- roomId is the ID of the Campfire room the bot will connect to. +- apiKey is the API key for the Campfire user Gekko will connect using. +- account is the subdomain for the account that the room belongs to. + +### Redis beacon + +This is an advanced plugin only for programmers! If you are interested in this read more [here](https://github.com/askmike/gekko/blob/stable/docs/internals/plugins.md#redis-beacon). + + config.redisBeacon = { + enabled: false, + port: 6379, // redis default + host: '127.0.0.1', // localhost + // On default Gekko broadcasts + // events in the channel with + // the name of the event, set + // an optional prefix to the + // channel name. + channelPrefix: '', + broadcast: [ + 'small candle' + ] + } + +- enabled indicates whether this is on or off. +- port is the port redis is running on. +- host is the redis host. +- channelPrefix a string that Gekko will prefix all candles with. +- broadcast is a list of all events you want Gekko to publish. + diff --git a/docs/commandline/tradebot.md b/docs/commandline/tradebot.md new file mode 100644 index 0000000..8b6fce2 --- /dev/null +++ b/docs/commandline/tradebot.md @@ -0,0 +1,25 @@ +# Tradebot + +You can set Gekko up as a tradebot, this will instruct Gekko to: + +- Watch a live market. +- Run a strategy (in semi-realtime) over live market data. +- Automatically create buy/sell orders based on signals coming from the strategy. + +*As with everything in Gekko, the tradebot will make decisions based on the strategy selected/configured/created **by YOU**. If you end up losing money, you have no one to blame but yourself.* + +## Configuration + +First, set up Gekko for commandline usage (see [this document](./about_the_commandline.md) for details). After that, configure the following plugins: + +- `config.watch` - the market to trade on. +- `candleWriter` - (optional) also store market data to disk. +- `tradingAdvisor` - configure the strategy and candle properties. +- `trader` - configure Gekko access to your exchange account. +- `performanceAnalyzer` - enable. + +Turn off the paperTrader (there can only be 1 trade plugin active per instance). + +Once done, run Gekko like so: + + node gekko --config your-config-file.js diff --git a/docs/extending/add_a_plugin.md b/docs/extending/add_a_plugin.md new file mode 100644 index 0000000..0576049 --- /dev/null +++ b/docs/extending/add_a_plugin.md @@ -0,0 +1,38 @@ +# Plugins + +A plugin is a low level module or plugin that can act upon events bubbling +through Gekko. If you want to have custom functionality so that your rocket +flies to the moon as soon as the price hits X you should create a plugin for it. + +All plugins live in `gekko/plugins`. + +Note that in order to use custom plugins, you have to run Gekko over [the commandline](../commandline/about_the_commandline.md). + +## Existing plugins: + +- Candle Store: save trades to disk. +- Mailer: mail trading advice to your gmail account. +- Pushbullet: send messages to Pushbullet devices. +- Telegram: send messages over Telegram. +- IRC bot: logs Gekko on in an irc channel and lets users communicate with it. +- Paper Trader: simulates trades and calculates profit over these (and logs profit). +- Trading advisor (internal): calculates advice based on market data. +- Redis beacon (advanced): [see below!](#redis-beacon) + +*And more! Take a look in the `gekko/plugins folder.`* + +## Implementing a new plugin + +If you want to add your own plugin you need to expose a constructor function inside +`plugins/[slugname of plugin].js`. The object needs methods based on which event you want +to listen to. All events can be found in [the events page](../architecture/events.md). + +You also need to add an entry for your plugin inside `plugins.js` which registers your plugin for use with Gekko. Finally you need to add a configuration object to `sample-config.js` with at least: + + config.[slug name of plugin] = { + enabled: true + } + +Besides enabled you can also add other configurables here which users can set themselves. + +That's it! Don't forget to create a pull request of the awesome plugin you've just created! diff --git a/docs/extending/add_an_exchange.md b/docs/extending/add_an_exchange.md new file mode 100644 index 0000000..fd60551 --- /dev/null +++ b/docs/extending/add_an_exchange.md @@ -0,0 +1,181 @@ +# Exchanges + +*This is a technical document about adding a new exchange to Gekko and Gekko Broker.* + +Gekko arranges all communication about when assets need to be bought or sold between the *strategy* and *Gekko Broker*. All differences between the different API's are abstracted away just below Gekko Broker inside an "exchange wrapper". This document describes all requirements for adding a new exchange wrapper (adding exchange support to Gekko). + +When you add a new exchange to Gekko you need to expose an object that has methods to query the exchange. This exchange file needs to reside in `gekko/exchange/wrappers` and the filename is the slug of the exchange name + `.js`. So for example the exchange integration with Binance is explained in `gekko/exchange/wrappers/binance.js`. + +Please use a npm module to query an exchange. This will separate the abstract API calls from the Gekko specific stuff (In the case of Bitstamp there was no module yet, so I [created one](https://www.npmjs.com/package/bitstamp)). + +Finally Gekko needs to know how it can interact with the exchange, so add a static method `getCapabilities()` that returns it's properties. They are all described [here](#capabilities). + +## Gekko's expectations + +Gekko Broker implements an exchange like so: + + const Exchange = require('./wrapper' + [exchange slug]); + this.exchange = new Exchange({key: '', secret: '', username: '', currency: 'USD', asset: 'BTC'}); + +It will run the following methods on the exchange object: + +### getTrades + +With this single method implemented Gekko won't be able to do live trading, but it will be able to do paper trading and storing market data for backtesting. + + this.watcher.getTrades(since, callback, descending); + +If since is truthy, Gekko requests as much trades as the exchange can give (up to ~10,000 trades, if the exchange supports more you can [create an importer](../features/importing.md)). + +The callback expects an error and a `trades` object. Trades is an array of trade objects in chronological order (0 is older trade, 1 is newer trade). Each trade object needs to have: + +- a `date` property (unix timestamp in either string or int) +- a `price` property (float) which represents the price in [currency] per 1 [asset]. +- an `amount` property (float) which represent the amount of [asset]. +- a `tid` property (float) which represents the tradeID. + +## Gekko Broker's expectations + +The methods below are needed for Gekko to automatically trade on the exchange. + +### getTicker + + this.exchange.getTicker(callback) + +The callback needs to have the parameters of `err` and `ticker`. Ticker needs to be an object with at least the `bid` and `ask` property in float. + +### getFee + + this.exchange.getFee(callback); + +The callback needs to have the parameters of `err` and `fee`. Fee is a float that represents the amount the exchange takes out of the orders Gekko places. If an exchange has a fee of 0.2% this should be `0.002`. + +### getPortfolio + + this.exchange.getPortfolio(callback); + +The callback needs to have the parameters of `err` and `portfolio`. Portfolio needs to be an array of all currencies and assets combined in the form of objects, an example object looks like `{name: 'BTC', amount: 1.42131}` (name needs to be an uppercase string, amount needs to be a float). + +### getLotSize + + this.exchange.getLotSize(tradeType, amount, size, callback); + +The callback needs to have the parameters of `err` and `lot`. Lot needs to be an object with `amount` and `purchase` size appropriately for the exchange. In the event that the lot is too small, return 0 to both fields and this will generate a lot size warning in the portfolioManager. + +Note: This function is currently optional. If not implemented Gekko Broker will fallback to basic lot sizing mechanism it uses internally. However exchanges are not all the same in how rounding and lot sizing work, it is recommend to implement this function. + +### buy + + this.exchange.buy(amount, price, callback); + +### sell + + this.exchange.sell(amount, price, callback); + +This should create a buy / sell order at the exchange for [amount] of [asset] at [price] per 1 asset. The callback needs to have the parameters `err` and `order`. The order needs to be some id that can be passed back to `checkOrder`, `getOrder` and `cancelOrder`. + +### getOrder + + this.exchange.getOrder(order, callback); + +Will only be called on orders that have been completed. The order will be something that the manager previously received via the `sell` or `buy` methods. The callback should have the parameters `err` and `order`. Order is an object with properties `price`, `amount` and `date`. Price is the (volume weighted) average price of all trades necessary to execute the order. Amount is the amount of currency traded and Date is a moment object of the last trade part of this order. + +### checkOrder + + this.exchange.checkOrder(order, callback); + +The order will be something that the manager previously received via the `sell` or `buy` methods. The callback should have the parameters `err` and a `result` object. The result object will have two or three properties: + +- `open`: whether this order is currently in the orderbook. +- `executed`: whether this order has executed (filled completely). +- `filledAmount`: the amount of the order that has been filled. This property is only needed when both `open` is true and `completed` is false. + +### cancelOrder + + this.exchange.cancelOrder(order, callback); + +The order will be something that the manager previously received via the `sell` or `buy` methods. The callback should have the parameters `err`, `filled` and optionally a `fill` object, `filled` should be true if the order was fully filled before it could be cancelled. If the exchange provided how much was filled before the cancel this should be passed in the `fill object` as a `amountFilled` property. If the exchange provided how much of the original amount is still remaining, pass this as the `remaining` property instead. + +### roundPrice + + this.exchange.roundPrice(rawPrice); + +Should return a price. Rounds the price into a valid price Gekko Broker can later feed into a buy or sell order. *Note: if representation of the number is important for the exchange (for example if the exchange will not accept numbers expressed in scientific notation) you can return a string from this method (Gekko Broker will perform calculations so the string should be castable to a number.)* + +### roundAmount + + this.exchange.roundAmount(rawAmount); + +Should return an amount. Rounds the amount into a valid amount Gekko Broker can later feed into a buy or sell order. *Note: if representation of the number is important for the exchange (for example if the exchange will not accept numbers expressed in scientific notation) you can return a string from this method (Gekko Broker will perform calculations so the string should be castable to a number.)* + +## Gekko Broker's optional methods + +### isValidPrice + + this.exchange.isValidPrice(price); + +Should return true or false. If the exchange has restrictions on the price you can submit limit orders at. If there are no such restrictions you should not implement this method. + +### isValidLot + + this.exchange.isValidLot(price, amount); + +Should return true or false. If the exchange has restrictions on the lot size (order size expressed in "currency" amount) you can check for that here. If there are no such restrictions you should not implement this method. + +## Error handling + +It is the responsibility of the wrapper to handle errors and retry the call in case of a temporary error. Gekko exposes a retry helper you can use to implement an exponential backoff retry strategy. Your wrapper does need pass a proper error object explaining whether the call can be retried or not. If the error is fatal (for example private API calls with invalid API keys) the wrapper is expected to upstream this error. If the error is retryable (exchange is overloaded or a network error) an error should be passed with the property `notFatal` set to true. If the exchange replied with another error that might be temporary (for example an `Insufficient funds` error right after Gekko canceled an order, which might be caused by the exchange not having updated the balance yet) the error object can be extended with an `retry` property indicating that the call can be retried for n times but after that the error should be considered fatal. + +For implementation refer to the bitfinex implementation, in a gist this is what a common flow looks like: + +- (async call) `exchange.buy`, then +- handle the response and normalize the error so the retry helper understands it, then +- the retry helper will determine whether the call needs to be retried, then + - based on the error it will retry (for example `nonFatal` or `retry`) + - if no error it will pass it to your handle function that normalizes the output + +## Capabilities + +Each exchange *must* provide a `getCapabilities()` static method that returns an object with these parameters: + +- `name`: Proper name of the exchange +- `slug`: slug name of the exchange (needs to match filename in `gekko/exchanges/`) +- `currencies`: all the currencies supported by the exchange implementation in gekko. +- `assets`: all the assets supported by the exchange implementation in gekko. +- `pairs`: all allowed currency / asset combinations that form a market +- `maxHistoryFetch`: the parameter fed to the getTrades call to get the max history. +- `providesHistory`: If the getTrades can be fed a since parameter that Gekko can use to get historical data, set this to: + - `date`: When Gekko can pass in a starting point in time to start returning data from. + - `tid`: When Gekko needs to pass in a trade id to act as a starting point in time. + - `false`: When the exchange does not support to give back historical data at all. +- `fetchTimespan`: if the timespan between first and last trade per fetch is fixed, set it here in minutes. +- `tradable`: if gekko supports automatic trading on this exchange. +- `requires`: if gekko supports automatic trading, this is an array of required api credentials gekko needs to pass into the constructor. +- `forceReorderDelay`: if after canceling an order a new one can't be created straight away since the balance is not updated fast enough, set this to true (only required for exchanges where Gekko can trade). +- `gekkoBroker`: set this to "0.6.2" for now, it indicates the version of Gekko Broker this wrapper is compatible with. +- `limitedCancelConfirmation` set to true if (in any case) the wrapper does NOT include partial fill information in the response (which is passed as the cancel callback) + +Below is a real-case example how `bistamp` exchange provides its `getCapabilities()` method: + +``` +Trader.getCapabilities = function () { + return { + name: 'Bitstamp', + slug: 'bitstamp', + currencies: ['USD', 'EUR'], + assets: ['BTC', 'EUR'], + maxTradesAge: 60, + maxHistoryFetch: null, + markets: [ + { pair: ['USD', 'BTC'], minimalOrder: { amount: 1, unit: 'currency' } }, + { pair: ['EUR', 'BTC'], minimalOrder: { amount: 1, unit: 'currency' } }, + { pair: ['USD', 'EUR'], minimalOrder: { amount: 1, unit: 'currency' } } + ], + requires: ['key', 'secret', 'username'], + fetchTimespan: 60, + tid: 'tid', + gekkoBroker: '0.6.2', + limitedCancelConfirmation: false + }; +} +``` diff --git a/docs/extending/other_software.md b/docs/extending/other_software.md new file mode 100644 index 0000000..a49263f --- /dev/null +++ b/docs/extending/other_software.md @@ -0,0 +1,21 @@ +# Other Software + +Since Gekko version 0.4 Gekko can launch a process which exposes a web (REST and Websocket) API that can be used to control Gekkos (start a backtest, start a running Gekko, etc). This makes it easy for other people to build new functionality on top of Gekko without having to work with Gekko internals, the runtime (or even javascript at all). + +## List +### Backtesters +- [japonicus](https://github.com/Gab0/japonicus) +- [gekkoga](https://github.com/gekkowarez/gekkoga) +- [Gekko-BacktestTool](https://github.com/xFFFFF/GekkoBacktestTool) +- [Gekko Automated Backtest](https://github.com/tommiehansen/gab) +- [Gekko Warez Bruteforce Backtester](https://github.com/gekkowarez/bruteforce) + +### UI mode +- [Unofficial Material UI](https://github.com/H256/gekko-quasar-ui) +- [Controlling bots](https://github.com/CyborgDroid/gekko-python) + +### CLI mode +- [Batch generation of configuration files](https://github.com/bettimms/multi-gekko) + +### Plugins +- [Store trades in Google SpreadSheet](https://github.com/RJPGriffin/google-forms-gekko-plugin) diff --git a/docs/features/backtesting.md b/docs/features/backtesting.md new file mode 100644 index 0000000..2c3d8dc --- /dev/null +++ b/docs/features/backtesting.md @@ -0,0 +1,23 @@ +# Backtesting + +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 + +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 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: + +![screen shot of backtesting at an illiquid market](https://cloud.githubusercontent.com/assets/969743/24840243/8f307022-1d61-11e7-9964-e6614d7433ea.png) + +You can see a lot of "spikes" of the price moving up and down. These are not actually price fluctuations but simply trades that happen on both sides of the orderbook (a bid is taken then an ask is taken). How far it jumps up and down is the spread (between the asks and the bids). In these cases the statistics from the simulation won't be very accurate (unless you configured a higher slippage to account for the spread). This is unfortunately a limitation in Gekko's backtesting model. diff --git a/docs/features/importing.md b/docs/features/importing.md new file mode 100644 index 0000000..c15f3a8 --- /dev/null +++ b/docs/features/importing.md @@ -0,0 +1,11 @@ +# Importing + +In order to [backtest](./backtesting.md) your strategies you will need to have historical market data to test with. The easiest way of getting this data is importing it directly from the exchange using the Gekko UI (note that this is not supported at all exchanges, check [this list](../introduction/supported_exchanges.md) to see what exchanges Gekko can import from). + +You can start an import by navigating to the tab "Local tab" and scrolling to the bottom and click "Go to the importer". This brings you to the importing page with this at the bottom: + +![importing wizard](https://user-images.githubusercontent.com/969743/28596822-a79509cc-7192-11e7-9293-53066f598053.png) + +Once you configure the market and daterange you want to watch and click import Gekko will automatically download historical market data from the exchange: + +![gekko importer](https://user-images.githubusercontent.com/969743/28597211-914dbe32-7194-11e7-8352-60a69afdf846.png) diff --git a/docs/features/paper_trading.md b/docs/features/paper_trading.md new file mode 100644 index 0000000..5849c49 --- /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). diff --git a/docs/features/trading_bot.md b/docs/features/trading_bot.md new file mode 100644 index 0000000..7d44fff --- /dev/null +++ b/docs/features/trading_bot.md @@ -0,0 +1,22 @@ +# 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. + +## Preparation + +1. Make sure you are fully confident in your strategy! If you want to play around use either the [paper trader](./paper_trading.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 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! + +## 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. diff --git a/docs/gekko-broker/introduction.md b/docs/gekko-broker/introduction.md new file mode 100644 index 0000000..71f7770 --- /dev/null +++ b/docs/gekko-broker/introduction.md @@ -0,0 +1,130 @@ +# Gekko Broker + +Order execution library for bitcoin and crypto exchanges. This library is Gekko's execution engine for all live orders (simulated orders do not through Gekko Broker, they go through the paper trader). This library is intended for developers of trading systems in need for advanced order types on multiple exchanges over a unified API. + +## Introduction + +This library makes it easy to do (advanced) orders all crypto exchanges where Gekko can live trade at. See the complete list [here](https://gekko.wizb.it/docs/introduction/supported_exchanges.html). + +This library allows you to: + +- Get basic market data + - ticker (BBO) + - ~orderbook~ (TODO) + - historical trades +- Get portfolio data +- Do an (advanced) order: + - Submit the order to the exchange + - Receive events about the status of the order: + - submitted + - open (accepted) + - partially filled + - rejected + - completed + - Mutate the order + - cancel + - amend + - move + - move limit +- Tracks orders submitted through the library. + +## Status + +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 + +This library aims to offer advanced order types, even on exchanges that do not natively support them by tracking the market and supplementing native order support on specific exchanges. + +Working: + +- Base orders + - [Sticky Order](./sticky_order.md) + +TODO: + +- Base orders: + - Limit Order + - Market Order +- Triggers: + - 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 +- finish all order types and triggers (under todo) +- implement websocket support (per exchange) +- use native move API calls wherever possible (poloniex) diff --git a/docs/gekko-broker/sticky_order.md b/docs/gekko-broker/sticky_order.md new file mode 100644 index 0000000..db3ae58 --- /dev/null +++ b/docs/gekko-broker/sticky_order.md @@ -0,0 +1,48 @@ +# Sticky Order + +An advanced order that stays at the top of the book (until the optional limit). The order will automatically stick to the best BBO until the complete amount has been filled. + +TODO: + +- implement fallback for when this order is alone at the top, some spread before everyone else +- finalize API +- add more events / ways to debug +- pull ticker data out of this order market data should flow from the broker (so we can easier move to at least public websocket streams). + +## Example usage + + const Broker = require('gekko-broker'); + + const gdax = new Broker({ + currency: 'EUR', + asset: 'BTC', + + exchange: 'gdax', + + // Enables access to private endpoints. + // Needed to create orders and fetch portfolio + private: true, + + key: 'x', + secret: 'y', + passphrase: 'z' + }); + + gdax.portfolio.setBalances(console.log); + + const type = 'sticky'; + const amount = 0.5; + const side = 'buy'; + const limit = 6555; + + const order = gdax.createOrder(type, side, amount, { limit }); + order.on('statusChange', status => console.log(status)); + order.on('filled', result => console.log(result)); + order.on('completed', () => { + order.createSummary(summary => console.log) + }); + + // mutate like so + + // order.moveAmount(1); + // order.moveLimit(6666); \ No newline at end of file diff --git a/docs/gekko-broker/wrapper_api.md b/docs/gekko-broker/wrapper_api.md new file mode 100644 index 0000000..2d1efa1 --- /dev/null +++ b/docs/gekko-broker/wrapper_api.md @@ -0,0 +1,22 @@ +# Wrapper API + +Gekko Broker is a library that sits between trading applications and Gekko Broker Exchange Wrappers. Which means it has two APIs to communicate with other code: + +![diagram describing Gekko Broker API interface](https://user-images.githubusercontent.com/969743/41892153-566293a0-7941-11e8-9998-7a5b5b554ffd.png) + +This document descibres the API layer between the exchange wrappers and Gekko Broker. + +## Wrapper API spec + +The current API documentation is currently located [here](../extending/add_an_exchange.md). + +## Wrapper API Changelog + +### Gekko 0.5.x to Gekko (Broker) 0.6.0 + +NOTE: this API design might still have minor changes leading up to the release of Gekko 0.6. See [this thread](https://forum.gekko.wizb.it/thread-57279-post-59207.html) for more information. + +- The wrapper files are now nested different (from `gekko/exchanges` to `gekkobroker/wrappers` (which equals `gekko/exchange/wrappers` for Gekko users). +- cancelOrder now requires a second parameter to be passed (that indicates whether the order was filled before it was canceled), see [details](https://github.com/askmike/gekko/commit/0e301f7d66e24ec97327f5f01380f691cc2d3725#diff-dbfe320ca090e208be32459d98fc11ed). +- checkOrder now expects an object with a few properties to be returned, see [details](https://github.com/askmike/gekko/commit/e0d4a7362cd74b4b4f50759b1012ce489ea44a0c#diff-dbfe320ca090e208be32459d98fc11ed). +- Error handling has gotten a lot more complex, with an updated error interface between a retry system (provided by Gekko) and the exchange wrapper. [Read more here](https://github.com/askmike/gekko/commit/e0d4a7362cd74b4b4f50759b1012ce489ea44a0c#diff-dbfe320ca090e208be32459d98fc11ed). diff --git a/docs/installation/configuring_gekko_on_a_server.md b/docs/installation/configuring_gekko_on_a_server.md new file mode 100644 index 0000000..a9d56a6 --- /dev/null +++ b/docs/installation/configuring_gekko_on_a_server.md @@ -0,0 +1,148 @@ +# Configuring Gekko on a server + +*NOTE: unfortunately configuring Gekko to run on a server is hard. Unless you are running in a completely trusted network (home network) you WILL need to get an SSL certificate, upstream Gekko through a webserver, etc. I am creating an official Gekko service called [Gekko Plus](https://gekkoplus.com/) that will NOT require any of this stuff.* + +Gekko runs great headless (on a server, raspberry PI) but the default configuration assumes that you will be using a browser from the same machine to access the interface. + +# Installation + +Please see the normal [installing gekko](./installing_gekko.md) document, but before starting gekko please update the configuration as stated below: + +# Configuring Gekko + +In order to setup Gekko so that you can access it remotely you need to open and edit the following file: `gekko/web/vue/dist/UIconfig.js`. You need to configure this file according to your use case: + +- You want to Gekko headless in a trusted environment (eg. on a raspberry pi or old laptop in your home network) - [see here](#configuring-gekko-to-run-headless-in-a-trusted-environment). +- You want to run Gekko in the cloud on a server - [see here](#configuring-gekko-to-run-in-the-cloud). + +## Configuring Gekko to run headless in a trusted environment + +Edit the uiconfig file like so: + + const CONFIG = { + headless: true, + api: { + host: '0.0.0.0', + port: 3000, + }, + ui: { + ssl: false, + host: 'x.x.x.x', // Set this to the IP of the machine that will run Gekko + port: 3000, + path: '/' + }, + adapter: 'sqlite' + + } + +You can now access the Gekko UI by going to `http://x.x.x.x:3000` in a browser (change `x.x.x.x` with the IP of the machine that will run Gekko). + + +## Configuring Gekko to run in the cloud + +Important note: if you expose Gekko to the open internet (or on any non trusted network) you are recommended to put a secure reverse proxy (for example with both [SSL](#Obtaining-a-SSL-certificate) and [BasicAuth](#Create-htpasswd-for-basic-password-authentication)) in front of it. While we believe Gekko is hard to exploit, it allows for 24/7 backtesting which will drain your machine's resources (possible DoS). + +The following assumes you configured a reverse proxy, if you did not simply follow [these instructions](#Configuring-NGINX-as-a-reverse-proxy) to do so. + + + const CONFIG = { + headless: true, + api: { + host: '127.0.0.1', + port: 3000, + }, + ui: { + ssl: true, + host: 'gekko.example.com', + port: 443, + path: '/' // change this if you are serving from something like `example.com/gekko` + }, + adapter: 'sqlite' + } + + +## Configuring NGINX as a reverse proxy + +NGINX is a highly configurable, lightweight, yet easily deployed webserver allowing features such as a reverse proxying using secure sockets layer with authentication and much more. + +Installing NGINX using your Operating Systems package manager of choice is pretty straight forward. For Debian Linux it is a simple sudo apt-get install nginx + +Once NGINX is installed you will need to modify the configuration file. For Debian Linux the config is located at /etc/nginx/sites-enabled/default + + server { + listen 80; + listen [::]:80; + server_name gekko.example.com; + return 301 https://$server_name$request_uri; + } + + upstream websocket { + server localhost:3000; + } + + server { + listen 443 ssl; + listen [::]:443 ssl; + root /var/www/html; + + ssl_certificate /etc/nginx/ssl/nginx.crt; + ssl_certificate_key /etc/nginx/ssl/nginx.key; + + location / { + proxy_buffers 8 32k; + proxy_buffer_size 64k; + + proxy_pass http://websocket; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header Host $http_host; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-NginX-Proxy true; + + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + + proxy_read_timeout 86400s; + proxy_send_timeout 86400s; + + auth_basic "Restricted Content"; + auth_basic_user_file /etc/nginx/.htpasswd; + } + } + + +## Obtaining a SSL certificate + +Your OS may or may not ship with openssl preinstalled. In the case it doesn't, simply install openssl using your package manager of choice. eg: `sudo apt-get install openssl`. + +Below you can choose between creating a self signed certificate useful if you do not have a fqdn (fully qualified domain name), or if you by chance do have a fqdn you can use certbot to obtain a Let's Encrypt CA signed certificate. + + +### To create a self signed certificate: + + sudo mkdir /etc/nginx/ssl + sudo openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout /etc/nginx/ssl/nginx.key -out /etc/nginx/ssl/nginx.crt + + +### To obtain a Let's Encrypt CA signed certificate: + +Install certbot, a client to obtain signed ssl certificates for your domain. + + sudo apt-get install certbot + +Run the following command: + + certbot certonly --standalone -d example.com -d gekko.example.com + +Modify your NGINX config and replace the ssl lines with the following: + + ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem; + add_header Strict-Transport-Security "max-age=31536000"; + + +## Create htpasswd for basic password authentication + +Change username to desired username and enter password when prompted: + + printf "username:`openssl passwd -apr1`\n" >> /etc/nginx/.htpasswd diff --git a/docs/installation/installing_gekko.md b/docs/installation/installing_gekko.md new file mode 100644 index 0000000..5b9da4e --- /dev/null +++ b/docs/installation/installing_gekko.md @@ -0,0 +1,62 @@ +# Installing Gekko + +*NOTE: unfortunately installing and managing Gekko is hard. You will need to touch the commandline and install a few developer tools. I am creating an official Gekko service called [Gekko Plus](https://gekkoplus.com/) that will NOT require any installation.* + +- *Windows user? Please see the doc [installing Gekko on windows](./installing_gekko_on_windows.md) instead.* +- *Docker user? You can run Gekko in a docker container, see [installing Gekko using Docker](./installing_gekko_using_docker.md) instead.* +- *Server user? In order to run Gekko headless, see [configuring Gekko on a server](./configuring_gekko_on_a_server.md) instead.* + +Here is a video of me explaining how to install Gekko the easiest way possible: + +[![screen shot 2017-04-20 at 00 03 45](https://cloud.githubusercontent.com/assets/969743/25205894/e7f4ea64-255c-11e7-891b-28c080a9fbf2.png)](https://www.youtube.com/watch?v=R68IwVujju8) + + +To get Gekko running you need to do the following: + +- install nodejs +- install git +- download Gekko +- install Gekko & Gekko Broker dependencies + +## Installing nodejs + +Gekko requires [nodejs](https://nodejs.org/en/) to be installed. Go ahead and install this if it's not already (Gekko requires at least version 8.11.2). We advice to download the current LTS. + +## Installing git + +As part of Gekko's installation process git is used, see [this guide](https://www.linode.com/docs/development/version-control/how-to-install-git-on-linux-mac-and-windows/) for installation instructions. If you do not already have git on your system. + +## Downloading Gekko + +The recommended way of downloading Gekko is by using git. This makes keeping Gekko up to date a lot easier. Run this in a terminal: + + git clone git://github.com/askmike/gekko.git -b stable + cd gekko + +This will download the latest stable version of Gekko, remove the final `-b stable` part to download the current latest release (which might not be as stable). + +Alternatively you can manually download the latest stable version of Gekko on the [releases page](https://github.com/askmike/gekko/releases). + +## Installing Gekko's dependencies + +Once you have Gekko downloaded you need to install the dependencies, open your terminal and navigate to the gekko folder and run: + + npm install --only=production + +*NOTE: You may see a vulnerability warning from NPM, if you run npm audit with --force, Gekko will break. See [here](https://github.com/askmike/gekko/issues/2585#issuecomment-428450997).* + +You also need to install Gekko Broker's dependencies, run: + + cd exchange + npm install --only=production + cd .. + +## Starting Gekko + +After all the above you can start Gekko by running the following in your terminal: + + node gekko --ui + +## Updating Gekko + +See the [updating Gekko](./updating_gekko.md) doc. diff --git a/docs/installation/installing_gekko_on_raspberry_pi_2_or_3.md b/docs/installation/installing_gekko_on_raspberry_pi_2_or_3.md new file mode 100644 index 0000000..9f14d08 --- /dev/null +++ b/docs/installation/installing_gekko_on_raspberry_pi_2_or_3.md @@ -0,0 +1,74 @@ +# Installing Gekko on Raspberry Pi 2 or 3 + +This manual is intended for a Raspberry Pi 2 or 3 with freshly installed Raspbian Stretch (Lite). + +To get Gekko running on your Pi you need to do the following: + +- update your package lists +- install nodejs +- download Gekko +- install Gekko's dependencies +- (optional) Configure your Pi as a headless server in your local network + +## Update your package lists + + sudo apt-get update + sudo apt-get upgrade + +## Installing nodejs + +We recommend installing the latest LTS Version of Node, currently 8.x.x + + curl -sL https://deb.nodesource.com/setup_8.x | sudo -E bash - + sudo apt-get install -y nodejs + +Important: Ignore the errors! +=> It automatically builds from source instead, it can take over 10 minutes! Do not abort the process + +## Downloading Gekko + +The recommended way of downloading Gekko is by using git. This makes keeping Gekko up to date a lot easier. Run this in a terminal: + + sudo apt-get install git + git clone git://github.com/askmike/gekko.git -b stable + cd gekko + +This will download the latest stable version of Gekko, remove the final `-b stable` part to download the current latest release (which might not be as stable). + +## Installing Gekko's dependencies + +Once you have Gekko downloaded you need to install the dependencies, open your terminal and navigate to the gekko folder and run: + + npm install --only=production + cd exchange + npm install --only=production + cd .. + +## Configure your Pi as a headless server + + cd web/vue/dist + nano UIconfig.js + +Set headless to true + +Set api.host to 0.0.0.0 + +Set ui.host to Pi's IP address (e.g. 192.168.1.74) + +## Starting Gekko + +After all the above you can start Gekko by running the following in your terminal: + + node gekko --ui + +Open your browser and type in Pi's IP:port (default port: 3000) + +## Updating Gekko + +See the [updating Gekko](./updating_gekko.md) doc. + +## Run gekko in background +If you access Pi headless via SSH, you have to use a software like tmux to run Gekko in background + +## Installing Gekko on Raspberry Pi 1 +It is possible to run Gekko on Raspberry Pi 1, but you have to manually install Nodejs armv6 version diff --git a/docs/installation/installing_gekko_on_windows.md b/docs/installation/installing_gekko_on_windows.md new file mode 100644 index 0000000..c5b4989 --- /dev/null +++ b/docs/installation/installing_gekko_on_windows.md @@ -0,0 +1,77 @@ +# Installing Gekko on windows + +*NOTE: unfortunately installing and managing Gekko is hard. You will need to touch the commandline and install a few developer tools. I am creating an official Gekko service called [Gekko Plus](https://gekkoplus.com/), it will NOT require any installation.* + +### Note: +#### Windows does not natively support TA-lib. We are currently working on implementing the Tulip Indicators Library, which will provide similar functionality (see [#708](https://github.com/askmike/gekko/issues/708)). +#### For advanced users only: As a temporary workaround until [#708](https://github.com/askmike/gekko/issues/708) is implemented, TA-lib can be used on Windows through Bash on Windows 10. See "Installing Gekko on Windows with bash on Windows 10" + +Here is a youtube video I made that shows exactly how to set up Gekko: + +[![screen shot 2017-04-20 at 00 03 45](https://cloud.githubusercontent.com/assets/969743/25205894/e7f4ea64-255c-11e7-891b-28c080a9fbf2.png)](https://www.youtube.com/watch?v=R68IwVujju8) + +To get Gekko running on Windows you need to do the following: + +- install nodejs +- download Gekko +- install Gekko's dependencies + +## Install nodejs + +Gekko runs on nodejs so we have to install that first. Head over the [nodejs homepage](http://nodejs.org/) and install the LTS version of nodejs. + +## Install Gekko + +The easiest way to download Gekko is to go to the [Github repo](https://github.com/askmike/gekko) and click on the 'zip' button at the top. Once you have downloaded the zip file it's the easiest to extract it. When you have done that we can begin with the cool stuff: + +### Open up command line + +* Start +* Type in 'cmd.exe' +* Press enter + +### Install dependencies + +(After every command, press enter) + +First navigate to Gekko: + + cd Downloads + cd gekko-stable + cd gekko-stable + +Install Gekko's dependencies: + + npm install --only=production + +*NOTE: You may see a vulnerability warning from NPM, if you run npm audit with --force, Gekko will break. See [here](https://github.com/askmike/gekko/issues/2585#issuecomment-428450997).* + +Install Gekko Broker's dependencies: + + cd exchange + npm install --only=production + cd .. + +### Install Tulip Indicators + +If you are using Windows you will need to install python and the VC++ 2015 build tool, the easiest way to do this is through node as an administrator: + + npm install windows-build-tools --global --production + +Once your build tools are installed, or for other operating systems + + npm install tulind --only=production + +### Starting Gekko + + node gekko --ui + +Your browser should automatically open with the UI. If it doesn't, manually browse to [http://localhost:3000](http://localhost:3000). + +### Stopping Gekko + +In the command line hold `ctrl` + `c`. + +### Updating Gekko + +See the [updating Gekko](./updating_gekko.md) doc. diff --git a/docs/installation/installing_gekko_on_windows_with_bash_on_windows_10.md b/docs/installation/installing_gekko_on_windows_with_bash_on_windows_10.md new file mode 100644 index 0000000..441636d --- /dev/null +++ b/docs/installation/installing_gekko_on_windows_with_bash_on_windows_10.md @@ -0,0 +1,91 @@ +# Installing Gekko on Windows with bash on Windows 10 + +*NOTE: unfortunately installing and managing Gekko is hard. You will need to touch the commandline and install a few developer tools. I am creating an official Gekko service called [Gekko Plus](https://gekkoplus.com/), it will NOT require any installation.* + +### Note: +#### This guide is for advance users only! This is a temporary solution until [#708](https://github.com/askmike/gekko/issues/708) is implemented +#### You must be running a 64-bit version of Windows 10 to use Bash on Windows 10 +#### TA-lib does not support Windows 10, but it can be used on Bash on Windows 10 by following these instructions + + +## To get Gekko running on Bash on Windows you need to do the following: + +- enable Bash on Windows 10 +- install nodejs +- install git +- download and install Gekko and its dependencies +- edit the handle.js file +- install the correct version of TA-lib + +## Enable Bash on Windows 10 + +To install Bash shell on your Windows 10 PC, do the following (see [here](https://www.windowscentral.com/how-install-bash-shell-command-line-windows-10) for more info): + +- Open Settings. +- Click on Update & security. +- Click on For Developers. +- Under "Use developer features", select the Developer mode option to setup the environment to install Bash. +- On the message box, click Yes to turn on developer mode. +- After the necessary components install, you'll need to restart your computer. +- Once your computer reboots, open Control Panel. +- Click on Programs. +- Click on Turn Windows features on or off. +- Check the Windows Subsystem for Linux (beta) option. +- Click OK. +- Once the components installed on your computer, click the Restart now button to complete the task. + +After your computer restarts, you will notice that Bash will not appear in the "Recently added" list of apps, this is because Bash isn't actually installed yet. Now that you have setup the necessary components, use the following steps to complete the installation of Bash: + +- Open Start, do a search for bash.exe, and press Enter. +- On the command prompt, type y and press Enter to download and install Bash from the Windows Store. + +Then you'll need to create a default UNIX user account. This account doesn't have to be the same as your Windows account. Enter the username in the required field and press Enter (you can't use the username "admin"). Close the "bash.exe" command prompt. +Now that you completed the installation and setup, you can open the Bash tool from the Start menu like you would with any other app. + +## Install nodejs + +Gekko runs on nodejs so we have to install that first. + +Open up bash and install node.js: (taken from [here](https://nodejs.org/en/download/package-manager/#debian-and-ubuntu-based-linux-distributions)) + +``` +sudo apt-get update +curl -sL https://deb.nodesource.com/setup_8.x | sudo -E bash - +sudo apt-get install -y nodejs +sudo apt-get install -y build-essential +``` + +## Install Gekko and its dependencies + +The easiest way to download Gekko is to use Git: + +- Install Git + +`sudo apt-get install git` + +- Download and install gekko in you home directory (/home/yourusername): (taken from [here](https://gekko.wizb.it/docs/installation/installing_gekko.html)): + +``` +git clone git://github.com/askmike/gekko.git +cd gekko +npm install --only=production +cd exchange +npm install --only=production +cd .. +``` + +*NOTE: You may see a vulnerability warning from NPM, if you run npm audit with --force, Gekko will break. See [here](https://github.com/askmike/gekko/issues/2585#issuecomment-428450997).* + +### Starting Gekko + +`node gekko --ui` + +Your browser should automatically open with the UI. If it doesn't, manually browse to [http://localhost:3000](http://localhost:3000). + +### Stopping Gekko + +In bash hold `ctrl` + `c`. + +### Updating Gekko + +See the [updating Gekko](./updating_gekko.md) doc. diff --git a/docs/installation/installing_gekko_using_docker.md b/docs/installation/installing_gekko_using_docker.md new file mode 100644 index 0000000..c491015 --- /dev/null +++ b/docs/installation/installing_gekko_using_docker.md @@ -0,0 +1,25 @@ +# Installing Gekko using Docker + +Installing and running gekko in a docker container for use on the same machine is simple with the following commands: + +``` +$ docker-compose build +$ docker-compose up -d +``` + +You can now find your gekko instance running on `localhost:3000`. + +## Installing for external access + +However if you want to run Gekko on another machine (in the cloud, on a server), you need to specify the host machine and its port like so: + +``` +$ docker-compose build +$ HOST=mydomain.com PORT=3001 docker-compose up -d +``` + +You can now find your gekko instance running on `mydomain.com:3001`. + +If running behind an SSL-terminating proxy, make sure to set `USE_SSL=1` to tell the Gekko to use the HTTPS and WSS protocols instead of the default HTTP and WS protocols. + +To see logs: `docker logs -f gekko_gekko_1`. View which dockers are running by executing `docker ps`. diff --git a/docs/installation/updating_gekko.md b/docs/installation/updating_gekko.md new file mode 100644 index 0000000..1faa210 --- /dev/null +++ b/docs/installation/updating_gekko.md @@ -0,0 +1,42 @@ +# Updating Gekko + +## Prepare + +Before updating your local version of Gekko it's good to keep a few things in mind: + +- If you have configured a UIconfig (to run Gekko on a server for example) make a copy of the UIconfig file (stored in `gekko/web/vue/dist/`) first and call it something else (such as `my-UIconfig.js`). After that revert the file to it's original state (which can be found in `gekko/web/baseUIconfig.js/`) prior to updating (or git will complain). +- Check the changelog (found in the [releases page](https://github.com/askmike/gekko/releases)) for breaking changes) regarding: + - (in case you changed the UIconfig) for changes in the UIconfig. + - (in case you were using non standard strategies) strategy API, to make sure your strategies will run in the new version. + - any other breaking changes specified inside the release log (such as required node and or browser versions). + +## Updating to a new stable release + +Run the following commands inside the Gekko directory: + + git checkout stable + git pull + npm install --only=production + cd exchange + npm install --only=production + cd .. + +## Updating the develop branch + +The develop branch contains the most recent code as we are working on Gekko. In most scenarios this should considered less stable. But note that once a bug is found and fixed the fixes are applied here immediately while it takes the release of a new version to have these changes available in the stable branch. + + git checkout develop + git pull + npm install --only=production + cd exchange + npm install --only=production + cd .. + +### Compiling the frontend on the develop branch + +If you are using the UI and it changed since the latest release you need to manually recompile it since (as of v0.6.2) Gekko only ships compiled frontends with new releases. + + cd web/vue + npm install + npm run build + cd ../.. \ No newline at end of file diff --git a/docs/internals/architecture.md b/docs/internals/architecture.md new file mode 100644 index 0000000..b884858 --- /dev/null +++ b/docs/internals/architecture.md @@ -0,0 +1,44 @@ +# Gekko's architecture + +![Gekko architecture](https://wizb.it/gekko/static/architecture.jpg) + +Every Gekko instance has two core components: + +- A market +- A GekkoStream + +Communication between those two components is handled by Node.JS' [Stream API](https://nodejs.org/api/stream.html). The Market implements a Readable Stream interface while the GekkoStream implements a Writeable Stream interface. + +## A Market + +All markets in Gekko eventually output `candle` data. Where these candles come from and how old they are does not matter to the GekkoStream they get piped into. On default Gekko looks for markets in the [`core/markets/` directory](https://github.com/askmike/gekko/tree/stable/core/markets). The top orange block in the picture is a BudFox market (the default semi-realtime market that gets live data from exchanges). + +Example Markets that come included with Gekko are: + +- **BudFox** (a realtime market): this market stays on indefinitely and outputs candles from the live markets in semi-realtime (cryptocurrency exchanges run 24/7). +- **backtest**: this market reads candles from a database and outputs them as fast as the GekkoStream is able to consume them (the GekkoStream will run TA strategies over these candles). +- **importer**: the importer market will fetch historical data from an exchange and pass on (to the GekkoStream who inserts them in a database). + +## A GekkoStream + +A GekkoStream is nothing more than a collection of [plugins](../commandline/plugins.md). Plugins are simple modules that can subscribe to events, and do something based on event data. The most basic event every GekkoStream has is the "candle" event, this event comes from the market. + +However **plugins are allowed to broadcast their own events, which other plugins can subscribe to**. An example of this is the `tradingAdvisor` plugin. This plugin will implement a [strategy](../strategies/introduction.md) that will be fed candle data. As soon as the strategy suggests to take a certain position in the market ("I detect an uptrend, I advice to go **long**") it will broadcast an `advice` event. The `paperTrader` is a plugin that simulates trading using these advices, the `trader` is a plugin that creates real market orders based on these advices. You can decide to only turn the `paperTrader` on or to turn the `trader` on (you now have a live trading bot). + +When you run a backtest using Gekko the following things happen: + +- Gekko configures a `backtest` market. +- Gekko loads all configured plugins that are supported in the `backtest` mode* into a GekkoStream. +- Gekko pipes the market into the GekkoStream and voila! + +\*Gekko refuses to load plugins that are unsupported in specific modes. During backtests you **never** want to enable the real trader to enter market orders. Because if you would the advice would be based on specific moments in the backtest, not on the current state of the market. + +## Plugins & Adapters + +Those two core components describe the majority of Gekko's flow. A lot "core functionality" like saving candles to disk are simply plugins that push all candles to a database. + +## Separated architecture + +The modular nature of Gekko makes it very dynamic and allows for rapidly creating new plugins. However there is an ugly side to this story: + +The `tradingAdvisor` runs TA strategies against a market. The problem however is that most TA indicators need some history before they can give accurate results. If you want to use an EMA (exponential moving average), you need some history to base the initial average on. But because the tradingAdvisor doesn't know what market data is going to be made available later by the market, it needs to do some fetching itself and compare that to locally available market data (stored in the local database) to see if it can stitch the two sources. diff --git a/docs/internals/budfox.md b/docs/internals/budfox.md new file mode 100644 index 0000000..09bca04 --- /dev/null +++ b/docs/internals/budfox.md @@ -0,0 +1,41 @@ +# BudFox + +**Similar to the [movie Wallstreet](https://en.wikipedia.org/wiki/Wall_Street_(1987_film)), Gekko delegates the dirty work of getting fresh data to Bud Fox. Bud Fox delivers the data to Gekko who uses this data to make investment decisions.** + +Whenever Gekko works with realtime market data, it spawns a BudFox to fetch and transform the data for every market (exchange + asset + pair, for example: `bitstamp USD/BTC`). Bud Fox will keep on fetching data from the market in semi-realtime, turn historical trades into minutely candles (and make sure every minute of data has a candle). + +BudFox exposes a stream of `candles` which are fed to Gekko. + +## Advanced Usage + +BudFox is a small part of Gekko's core that aggregates realtime market data from any supported exchange into a readable stream of candles. Example usage: + + var config = { + exchange: 'Bitstamp', + currency: 'USD', + asset: 'BTC' + } + + new BudFox(config) + .start() + // convert JS objects to JSON string + .pipe(new require('stringify-stream')()) + // output to standard out + .pipe(process.stdout); + +Outputs: + + {"start":"2015-02-02T23:08:00.000Z","open":238.21,"high":239.35,"low":238.21,"close":238.66,"vwp":8743.778447997309,"volume":203.6969347,"trades":56} + {"start":"2015-02-02T23:09:00.000Z","open":239.03,"high":240,"low":238.21,"close":239.19,"vwp":8725.27119145289,"volume":323.66383462999994,"trades":72} + {"start":"2015-02-02T23:10:00.000Z","open":239.19,"high":239.8,"low":234.68,"close":235,"vwp":6664.509955946812,"volume":114.67727173,"trades":48} + {"start":"2015-02-02T23:11:00.000Z","open":237.77,"high":238.51,"low":235,"close":238.1,"vwp":3158.835462414369,"volume":41.47081054999999,"trades":28} + {"start":"2015-02-02T23:12:00.000Z","open":237,"high":238,"low":236.78,"close":237.9,"vwp":1634.5173557116634,"volume":70.58755061,"trades":22} + {"start":"2015-02-02T23:13:00.000Z","open":237.95,"high":238.49,"low":237.95,"close":238.49,"vwp":604.219141331534,"volume":12.196531389999999,"trades":7} + {"start":"2015-02-02T23:14:00.000Z","open":238.51,"high":241,"low":237.89,"close":241,"vwp":7610.305142999085,"volume":579.5321983399998,"trades":67} + {"start":"2015-02-02T23:15:00.000Z","open":238.12,"high":239.76,"low":238.12,"close":239.1,"vwp":1828.5872875471068,"volume":31.16232463,"trades":17} + {"start":"2015-02-02T23:16:00.000Z","open":239.1,"high":239.76,"low":239.1,"close":239.67,"vwp":1339.3753800771717,"volume":5.56431998,"trades":12} + {"start":"2015-02-02T23:17:00.000Z","open":239.27,"high":239.99,"low":239.25,"close":239.92,"vwp":1519.3392752690336,"volume":6.984999999999999,"trades":14} + {"start":"2015-02-02T23:18:00.000Z","open":239.92,"high":239.98,"low":238.98,"close":238.98,"vwp":4162.807256131301,"volume":21.17212333,"trades":29} + {"start":"2015-02-02T23:19:00.000Z","open":239,"high":239,"low":238.15,"close":238.33,"vwp":1627.2581467076204,"volume":31.682705360000003,"trades":15} + {"start":"2015-02-02T23:20:00.000Z","open":238.33,"high":239.95,"low":238.33,"close":239,"vwp":3648.661808492067,"volume":128.35564560999998,"trades":23} + // etc.. diff --git a/docs/internals/events.md b/docs/internals/events.md new file mode 100644 index 0000000..95d440c --- /dev/null +++ b/docs/internals/events.md @@ -0,0 +1,359 @@ +# 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](./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. + +Note that all events from Gekko come from a plugin (with the exception of the `candle` event, which comes from the market), and no plugin is required for Gekko to run, this means it might be possible that some events are never broadcasted since their originating plugin is not active. If a plugin wants to listen to an event that will never be broadcasted (because of a lack of another plugin) this will be warned in the console like so: + + (WARN): Paper Trader wanted to listen to the tradingAdvisor, however the tradingAdvisor is disabled. + +## List of events emitted by standard plugins + +- [candle](#candle-event): Every time Gekko calculates a new one minute candle from the market. +- [stratWarmupCompleted](#stratWarmupCompleted-event): When the strategy is done warming up. +- [advice](#advice-event): Every time the trading strategy has a new trading signal. +- [stratUpdate](#stratUpdate-event): Every time the strategy has processed a new strat candle. +- [stratNotification](#stratNotification-event): Every time the strategy emit new strategy notification. +- [tradeInitiated](#tradeInitiated-event): Every time a trading plugin (either the live trader or the paper trader) is going to start a new trade (buy or sell). +- [tradeCompleted](#tradeCompleted-event): Every time a trading plugin (either the live trader or the paper trader) has completed a trade. +- [tradeAborted](#tradeAborted-event): Every time a trading plugin (either the live trader or the paper trader) has NOT acted on new advice (due to insufficient funds or a similar reason). +trader) has NOT acted on new advice (due to unsufficiant funds or a similar reason). +- [tradeErrored](#tradeErrored-event): Every time the live trader was unable to execute an initialized. +- [tradeCancelled](#tradeCancelled-event): Every time the live trader cancelled a not yet executed trade. +- [portfolioChange](#portfolioChange-event): Every time the content of the portfolio has changed. +- [portfolioValueChange](#portfolioValueChange-event): Every time value of the portfolio has changed. +- [performanceReport](#performanceReport-event): Every time the profit report was updated. +- [roundtrip](#roundtrip-event): Every time a new roundtrip has been completed. +- [triggerCreated](#triggerCreated-event): Every time a trader has created a new trigger. +- [triggerFired](#triggerFired-event): Every time a created trigger has fired. +- [triggerAborted](#triggerAborted-event): Every time a created trigger has been aborted due to new advice. + +Beside those there are also two additional market events that are only emitted when Gekko is running in realtime mode (NOT during a backtest for performance reasons). + +- [marketStart](#marketStart-event): Once, when the market just started. +- [marketUpdate](#marketUpdate-event): Whenever the market has fetched new raw market data. + +### candle event + +- What: An object containing a one minute candle from the market. +- When: In liquid markets roughly every minute. +- Subscribe: Your plugin can subscribe to this event by registering the `processCandle` method. +- Async: When subscribing to this event the second argument will be a callback which you are expected to call when done handling this event. +- Notes: + - Depending on the gekko configuration these candles might be historical on startup. If this is a concern for consumers, make sure to deal with this properly. + - In illiquid markets (of less than a trade per minute) Gekko will calculate these candles in batches and a few might come at the same time. + - These are always one minute candles, this is the lowest level of market data flowing through a gekko stream. +- Example: + { + start: [moment object of the start time of the candle], + open: [number, open of candle], + high: [number, high of candle], + low: [number, low of candle], + close: [number, close of candle], + vwp: [number, average weighted price of candle], + volume: [number, total volume volume], + trades: [number, amount of trades] + } + +### stratWarmupCompleted event + +- What: An object signaling that the strategy is now completely warmed up +and will start signaling advice. +- When: Once the strategy consumed more market data than defined by the required history. +- Subscribe: You can subscribe to this event by registering the `processWarmupCompleted` method. +- Notes: + - This event is triggered on init when the strategy does not require any history (and thus no warmup time). +- Example: + { + start: [moment object of the start time of the first candle after the warmup], + } + +### stratCandle event + +- What: An object describing an updated strat candle the strat has processed. +- When: when the strategy is initialized is started. +- Subscribe: You can subscribe to this event by registering the `processStratCandle` method. +- Notes: + - This is the candle that the strategy sees: if you configured the candleSize to 60 (minutes) this event will contain a 60 minute candle. + - Strat Candles are emitted while the strategy is still warming up (before the `stratWarmupCompleted` event). +- Example: + { + start: [moment object of the start time of the candle], + open: [number, open of candle], + high: [number, high of candle], + low: [number, low of candle], + close: [number, close of candle], + vwp: [number, average weighted price of candle], + volume: [number, total volume volume], + trades: [number, amount of trades] + } + + +### stratUpdate event + +- What: An object describing updated state of the strategy based on a new strat candle. +- When: when the strategy has +- Subscribe: You can subscribe to this event by registering the `processStratUpdate` method. +- Notes: + - Strat updates are emitted while the strategy is still warming up (before the `stratWarmupCompleted` event). +- Example: + { + date: [moment object of the start time of the candle], + indicators: { + mymacd: [number, result of running this indicator over current candle] + } + } + +### stratNotification event + +- What: An object describing new notification from your strategy +- When: when the strategy emit using `this.notify()` function +- Subscribe: You can subscribe to this event by registering the `processStratNotification` method. +- Example: + { + date: [moment object of the start time of the candle], + content: [String content notification in strategy] + } + + +### advice event + +- What: An advice from the strategy, the advice will either be LONG or SHORT. +- When: This depends on the strategy and the candleSize. +- Subscribe: You can subscribe to this event by registering the `processAdvice` method. +- Example: + { + recommendation: [position to take, either long or short], + date: [moment object of this advice], + id: [string identifying this unique trade], + trigger: { + type: [type of trigger, currently always "trailingStop"], + // optional parameters per type of trigger + } + } + +### tradeInitiated event + +- What: An object signaling that a new trade will be executed. +- When: At the same time as the advice event if the trader will try to trade. +- Subscribe: You can subscribe to this event by registering the `processTradeInitiated` method. +- Example: + { + id: [string identifying this unique trade], + adviceId: [number specifying the advice id this trade is based on], + action: [either "buy" or "sell"], + date: [moment object, exchange time trade completed at], + portfolio: [object containing amount in currency, asset and total balance], + balance: [number, total worth of portfolio] + } + +### tradeAborted event + +- What: An object signaling the fact that the trader will ignore the advice. +- When: At the same time as the advice event if the trader will NOT try to trade. +- Subscribe: You can subscribe to this event by registering the `processTradeAborted` method. +- Example: + { + id: [string identifying this unique trade], + adviceId: [number specifying the advice id this trade is based on], + action: [either "buy" or "sell"], + date: [moment object, exchange time trade completed at], + reason: [string explaining why the trade was aborted] + } + +### tradeCancelled event + +- What: An object signaling the fact that the a trade originally initiated was now cancelled. +- When: After a receiving a tradeInitiated event and than a advice event (without a tradeCompleted event between them). +- Subscribe: You can subscribe to this event by registering the `processTradeCanceled` method. +- Example: + { + id: [string identifying this unique trade], + adviceId: [number specifying the advice id this trade is based on], + date: [moment object, exchange time trade completed at] + } + +### tradeErrored event + +- What: An object singaling the fact that the a trade orginially initiated was now cancelled +- When: After a tradeInitiated event +- Subscribe: You can subscribe to this event by registering the `processTradeErrored` method. +- Example: + { + id: [string identifying this unique trade], + adviceId: [number specifying the advice id this trade is based on], + date: [moment object, exchange time trade completed at], + reason: [string explaining why the trade was aborted] + } + +### tradeCompleted event + +- What: Details of a completed trade. +- When: Some point in time after the tradeInitiated event. +- Subscribe: You can subscribe to this event by registering the `processTradeCompleted` method. +- Example: + { + id: [string identifying this unique trade], + adviceId: [number specifying the advice id this trade is based on], + action: [either "buy" or "sell"], + price: [number, average price that was sold at], + amount: [number, how much asset was trades (excluding "cost")], + cost: [number the amount in currency representing fee, slippage and other execution costs], + date: [moment object, exchange time trade completed at], + portfolio: [object containing amount in currency and asset], + balance: [number, total worth of portfolio], + feePercent: [the cost in fees], + effectivePrice: [executed price - fee percent, if effective price of buy is below that of sell you are ALWAYS in profit.] + } + +### portfolioChange event + +- What: An object containing new portfolio contents (amount of asset & currency). +- When: Some point in time after the advice event, at the same time as the tradeCompleted event. +- Subscribe: You can subscribe to this event by registering the `processPortfolioChange` method. +- Example: + { + currency: [number, portfolio amount of currency], + asset: [number, portfolio amount of asset], + } + +### portfolioValueChange event + +- What: An object containing the total portfolio worth (amount of asset & currency calculated in currency). +- When: Every time the value of the portfolio has changed, if the strategy is in a LONG position this will be every minute. +- Subscribe: You can subscribe to this event by registering the `processPortfolioValueChange` method. +- Example: + { + value: [number, portfolio amount of currency] + } + +### performanceReport event + +- What: An object containing a summary of the performance of the "tradebot" (advice signals + execution). +- When: Once every new candle. +- Subscribe: You can subscribe to this event by registering the `processPerformanceReport` method. +- Example: + { + startTime: Moment<'2017-03-25 19:41:00'>, + endTime: Moment<'2017-03-25 20:01:00'>, + timespan: 36000000, + market: -0.316304880517734, + balance: 1016.7200029226638, + profit: -26.789997197336106, + relativeProfit: -2.5672966425099304, + yearlyProfit: '-704041.12634599', + relativeYearlyProfit: '-67468.55576516', + startPrice: 945.80000002, + endPrice: 942.80838846, + trades: 10, + roundtrips: 5, + startBalance: 1043.5100001199999, + sharpe: -2.676305165560598 + } + +### roundtripInitiated event + +- What: A summary of a started roundtrip. +- When: After every tradeCompleted with action `buy`. +- Subscribe: You can subscribe to this event by registering the `processRoundtripInitiated` method. +- Example: + { + id: [string identifying this roundtrip], + entryAt: Moment<'2017-03-25 19:41:00'>, + entryPrice: 10.21315498, + entryBalance: 98.19707799420277, + exitAt: Moment<'2017-03-25 19:41:00'>, + exitPrice: 10.22011632, + exitBalance: 97.9692176, + duration: 3600000, + pnl: -0.2278603942027786, + profit: -0.2320439659276161, + } + +### roundtripUpdate event + +- What: An updated summary of a currently open roundtrip. +- When: On every candle for as long as the bot is in a long position. +- Subscribe: You can subscribe to this event by registering the `processRoundtripUpdate` method. +- Example: + { + id: [string identifying this roundtrip], + at: Moment<'2017-03-25 19:41:00'>, + duration: 3600000, + uPnl: -0.2278603942027786, + uProfit: -0.2320439659276161, + } + +### roundtrip event + +- What: A summary of a completed roundtrip (buy + sell signal). +- When: After every roundtrip: a completed sell trade event that superceded a buy sell trade event. +- Subscribe: You can subscribe to this event by registering the `processRoundtrip` method. +- Example: + { + id: [string identifying this roundtrip], + entryAt: Moment<'2017-03-25 19:41:00'>, + entryPrice: 10.21315498, + entryBalance: 98.19707799420277, + exitAt: Moment<'2017-03-25 19:41:00'>, + exitPrice: 10.22011632, + exitBalance: 97.9692176, + duration: 3600000, + pnl: -0.2278603942027786, + profit: -0.2320439659276161, + } + +### triggerCreated event + +- What: A summary of a created trigger. +- When: After a buy advice that includes a stop. +- Subscribe: You can subscribe to this event by registering the `processTriggerCreated` method. +- Example: + { + id: [string identifying this trigger], + date: Moment<'2017-03-25 19:41:00'>, + type: type: "trailingStop", + properties: { + initialPrice: 100, + trail: 10 + } + } + +### triggerFired event + +- What: A message indicating a created trigger has fired +- When: As soon as the trigger fired +- Subscribe: You can subscribe to this event by registering the `processTriggerFired` method. +- Example: + { + id: [string identifying this trigger], + date: Moment<'2017-03-25 19:41:00'> + } + +### triggerAborted event + +- What: A message indicating a created trigger has been aborted +- When: After an advice signal indicating a sell +- Subscribe: You can subscribe to this event by registering the `processTriggerAborted` method. +- Example: + { + id: [string identifying this trigger], + date: Moment<'2017-03-25 19:41:00'> + } + +### marketStart event + +- What: A moment object describing the first date of the market data. +- When: When the market is started. +- Subscribe: Your plugin can subscribe to this event by registering the `processMarketStart` method. +- Example: + [moment object describing the date of the first market data] + +### marketUpdate event + +- What: A moment object describing the point in time for up to which the market has market data. +- When: Every few seconds. +- Subscribe: Your plugin can subscribe to this event by registering the `processMarketUpdate` method. +- Example: + [moment object describing the date of the latest market data] diff --git a/docs/internals/gekko_ui.md b/docs/internals/gekko_ui.md new file mode 100644 index 0000000..a72156d --- /dev/null +++ b/docs/internals/gekko_ui.md @@ -0,0 +1,57 @@ +# Gekko UI + +When you launch Gekko UI, you start a basic nodejs webserver with 3 components: + +- It will serve frontend (HTML/CSS/JS) files for frontend written as a [vuejs app](https://vuejs.org/) (v2.5.16). +- It will will handle API requests as [koa](http://koajs.com/) (v1) routes, it will also start a websocket server used to broadcast messages in realtime (used for long lived processes Importing and Live Gekkos for example). The server API documentation can be found [here](./server_api.md). + +## Gekko UI Frontend + +The frontend is setup as a very basic vue app. Additionally the following libraries are used: + +- [Reconnecting websocket](https://github.com/joewalnes/reconnecting-websocket) +- [Moment](http://momentjs.com/) +- [d3.js](https://d3js.org/) +- [toml](https://github.com/BinaryMuse/toml-node) +- [humanizeDuration](https://github.com/EvanHahn/HumanizeDuration.js) + +The vue app itself uses the following libraries: + +- [marked](https://github.com/chjj/marked) +- [pug](https://github.com/pugjs) (all html is either written in pug of markdown) +- [vue-router](https://github.com/vuejs/vue-router) +- [vuex](https://github.com/vuejs/vuex) +- [superagent](https://github.com/visionmedia/superagent) (cross browser ajax) + +### Developing for the Gekko UI frontend + +You first need to install all developer dependencies so the frontend app can be recompiled on your machine. + + cd gekko/web/vue + npm install + +After this you can launch a hot reload version of the app which will automatically recompile the frontend and reload your browser: + + # path to webserver + cd gekko/web + # launch the server - we use this API + node server + + # path to vue app + cd vue + npm run serve + +Gekko UI is now served from port 8080, the webpack dev server will compile the vue app (in memory) and intercept all calls to the app itself (`/dist/js/app.xxx.js`) and serve the in memory app. It is important to note that this UI still talks to the API served from the `node server` command (on default http://localhost:3000/api). + + +If you have configured Gekko to run on non standards ports (using the UIconfig), you can have your config applied to the development environment by copying `gekko/web/vue/dist/UIconfig.js` to `gekko/web/vue/public/UIconfig.js`. + +### Compiling the Gekko UI frontend + +*Note: as part of the compilation process Gekko will reset the UIconfig in both `gekko/web/vue/dist/UIconfig.js` and `gekko/web/vue/public/UIconfig.js`. Only compile if you are ready to lose your personal changes.* + +When you are done developing you can compile the app using these instructions: + + # path to vue app + cd gekko/web/vue + npm run build diff --git a/docs/internals/plugins.md b/docs/internals/plugins.md new file mode 100644 index 0000000..8b5d5ba --- /dev/null +++ b/docs/internals/plugins.md @@ -0,0 +1,37 @@ +# 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). 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`. +- 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 + + // example: doesn't actually work.. + const alexa = require('alexa'); + + const MOON = 1000000; + + const Plugin = function() {} + + 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. 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 diff --git a/docs/internals/server_api.md b/docs/internals/server_api.md new file mode 100644 index 0000000..5bcfa3b --- /dev/null +++ b/docs/internals/server_api.md @@ -0,0 +1,795 @@ +# Server API + +The Gekko project has three major components: + + - [Actual Gekko instance](./architecture.md) + - A nodejs server that: + - starts and manages gekkos + - broadcasts all updates from all gekkos and tracks their overall state + - has API calls to query and manage historical data (by importing more for example) + - [A web UI (vue frontend project)](./gekko_ui.md) + +The nodejs server can also be used standalone as a webserver in combination with your own software (such as a new UI, a mobile app or a higher level backtester), see [other software](../extending/other_software.md) for examples). + +The server exposes two different APIs: A websocket API to push gekko updates and a REST API for everything else. + +When you run `node gekko --ui` it will automatically start a server. You can also do this manually by running: + + cd gekko/web + node server + +The server will now run on the host/port configured in `gekko/web/vue/public/UIConfig.js` (under api.host). + +## REST API + +### GET /api/info + +Get version information of current running Gekko Server. + +NOTE: The API does not have dedicated versioning, instead it uses the version of the complete Gekko project. + +**response** + +``` +{ "version": "0.6.0" } +``` + +### GET /api/strategies + +Get all strategies known to this Gekko. The value of the `params` key is a TOML string. + +**response** + +``` +[ + { + "name": "DEBUG_single-advice", + "params": "" + }, + { + "name": "DEBUG_toggle-advice", + "params": "" + }, + { + "name": "DEMA", + "params": "weight = 21\n\n[thresholds]\ndown = -0.025\nup = 0.025\n" + }, + { + "name": "MACD", + "params": "short = 10\nlong = 21\nsignal = 9\n\n[thresholds]\ndown = -0.025\nup = 0.025\npersistence = 1" + }, + // etc. +] +``` + +### GET /api/apiKeys + +Returns a list of exchanges that this Gekko has API keys stored for. + +NOTE: for security reasons the Gekko never exposes the API keys themselves. + +**response** + +``` +["bitstamp","bitfinex"] +``` + +### GET /api/imports + +Returns a list of currently running data imports. + +**response** + +``` +[ + { + "watch": { + "exchange": "poloniex", + "currency": "USDT", + "asset": "BTC" + }, + "id": "765606096903592", + "latest": "2018-04-03T07:13:00Z", + "from": "2018-04-03 05:13", + "to": "2018-07-03 05:13", + "done": false + } +] +``` + +### GET /api/gekkos + +Returns a list of currently running and archived Gekkos. + +**response** + +``` +{ + "live": { + "2018-07-03-13-14-papertrader-384222057313623": { + "mode": "realtime", + "config": { + // complete config that was passed when creating + }, + "id": "2018-07-03-13-14-papertrader-384222057313623", + "type": "leech", + "logType": "papertrader", + "active": true, + "stopped": false, + "errored": false, + "errorMessage": false, + "events": { + "initial": { + "candle": { + "id": 419824, + "start": "2018-07-03T04:47:00.000Z", + "open": 6664.79999887, + "high": 6664.79999887, + "low": 6659.99999989, + "close": 6659.99999989, + "vwp": 6662.402395386147, + "volume": 0.001202, + "trades": 3 + }, + "portfolioChange": { + "asset": 1, + "currency": 100 + }, + "portfolioValueChange": { + "balance": 6759.99999989 + } + }, + "latest": { + "candle": { + "id": 419849, + "start": "2018-07-03T05:12:00.000Z", + "open": 6643.21466865, + "high": 6656.58532982, + "low": 6643.01, + "close": 6656, + "vwp": 6643.884436854498, + "volume": 1.9966113400000003, + "trades": 15 + }, + "portfolioChange": { + "asset": 1, + "currency": 100 + }, + "portfolioValueChange": { + "balance": 6756 + } + } + }, + "start": "2018-07-03T05:14:23.582Z", + "latestUpdate": "2018-07-03T05:14:24.090Z" + } + }, + "archive": {} +} +``` + +### GET /api/exchanges + +Returns a list of supported exchanges with meta information (such as supported features and tradable markets). + +**response** + +``` +{ + "name": "Bitstamp", + "slug": "bitstamp", + "currencies": [ + "USD", + // and more + ], + "assets": [ + "BTC", + // and more + ], + "maxTradesAge": 60, + "maxHistoryFetch": null, + "markets": [ + { + "pair": [ + "USD", + "EUR" + ], + "minimalOrder": { + "amount": 5, + "unit": "currency" + }, + "precision": 2 + }, + // and more + ], + "requires": [ + "key", + "secret", + "username" + ], + "fetchTimespan": 60, + "tid": "tid", + "tradable": true +} +``` + +### POST /api/addApiKey + +Adds an API key for an exchange account to Gekko. As of now only 1 API keypair can be added per exchange. + +**request** + +``` +{ + "exchange": "bitstamp", + "values": { + "key": "x", + "secret": "y" + } +} +``` + +**response** + +``` +{ + "status": "ok" +} +``` + +### POST /api/removeApiKey + +Removes an API key from Gekko. + +**request** + +``` +{ + "exchange": "bitstamp" +} +``` + +**response** + +``` +{ + "status": "ok" +} +``` + +### POST /api/scan + +Scans a daterange to find all datasets for a specific market. + +**request** + +``` +{ + "watch": { + exchange: "bitstamp", + "currency": "USD", + "asset": "BTC" + } +} +``` + +**response** + +``` +[ + { + "to": 1464787560, + "from": 1464783960 + }, + { + "to": 1479115560, + "from": 1471112760 + } +] +``` + +### POST /api/scansets + +Scans the database for all datasets for all markets. + +**request** + +``` +{} +``` + +**response** + +``` +{ + "datasets": [ + { + "exchange": "bitfinex", + "currency": "USD", + "asset": "BTC", + "ranges": [ + { + "to": 1508551500, + "from": 1508493900 + } + ] + }, + { + "exchange": "binance", + "currency": "USDT", + "asset": "BTC", + "ranges": [ + { + "to": 1514351280, + "from": 1510632480 + }, + { + "to": 1522915680, + "from": 1522678080 + } + ] + }, + ], + "errors": [ + { + "exchange": "poloniex", + "currency": "BTC", + "asset": "BTC" + } + ] +} +``` + +### POST /api/backtest + +Perform a backtest. + +**request** + +*(a valid gekko config object)* + +Tweak the `config.backtestResultExporter.data` object to control what is included in the output. For example skip `stratCandles` if you only care about the final profit report. This way the server won't include megabytes of candle data in the output. + +``` +{ + "watch": { + "exchange": "poloniex", + "currency": "USDT", + "asset": "ETH" + }, + "paperTrader": { + "feeMaker": 0.25, + "feeTaker": 0.25, + "feeUsing": "maker", + "slippage": 0.05, + "simulationBalance": { + "asset": 1, + "currency": 100 + }, + "reportRoundtrips": true, + "enabled": true + }, + "tradingAdvisor": { + "enabled": true, + "method": "MACD", + "candleSize": 60, + "historySize": 10 + }, + "MACD": { + "short": 10, + "long": 21, + "signal": 9, + "thresholds": { + "down": -0.025, + "up": 0.025, + "persistence": 1 + } + }, + "backtest": { + "daterange": { + "from": "2016-06-01T11:57:00Z", + "to": "2016-11-13T14:57:00Z" + } + }, + "backtestResultExporter": { + "enabled": true, + "writeToDisk": false, + "data": { + "stratUpdates": false, + "roundtrips": true, + "stratCandles": true, + "stratCandleProps": [ + "open" + ], + "trades": true + } + }, + "performanceAnalyzer": { + "riskFreeReturn": 2, + "enabled": true + } +} +``` + +**response** + +``` +{ + "performanceReport": { + "startTime": "2018-04-02 14:08:00", + "endTime": "2018-04-05 04:08:00", + "timespan": "3 days", + "market": -2.159980297428632, + "startBalance": 7083.86, + "balance": 7424.849452897501, + "profit": 340.989452897501, + "relativeProfit": 4.813610840664566, + "yearlyProfit": 48210.518806418906, + "relativeYearlyProfit": 680.5684867631334, + "startPrice": 6983.86, + "endPrice": 6833.01, + "trades": 3, + "exposure": 0.3709677419354839, + "sharpe": null, + "downside": null, + "alpha": 343.14943319492966 + }, + "roundtrips": [ + { + "id": 0, + "entryAt": 1522685280, + "entryPrice": 7019.99, + "entryBalance": 7119.6899337771, + "exitAt": 1522768080, + "exitPrice": 7365, + "exitBalance": 7447.19106625, + "duration": 82800000, + "pnl": 327.5011324729003, + "profit": 4.599935327508788 + }, + // and more + ], + "stratCandles": [ + { + "open": 6990.06, + "start": 1522678080 + }, + // and more + ], + "trades": [ + { + "id": "trade-1", + "adviceId": "advice-1", + "action": "buy", + "cost": 0.30000000000000027, + "amount": 1.01420229, + "price": 7019.99, + "portfolio": { + "asset": 1.08661475, + "currency": 0 + }, + "balance": 7119.6899337771, + "date": 1522685280 + }, + // and more + ] +} +``` + +### POST /api/import + +Start a data import. + +**request** + +*(a valid gekko config object)* + + +``` +{ + "watch": { + "exchange": "poloniex", + "currency": "USDT", + "asset": "BTC" + }, + "importer": { + "daterange": { + "from": "2018-04-03 05:46", + "to": "2018-07-03 05:46" + } + }, + "candleWriter": { + "enabled": true + } +} +``` + +**response** + +NOTE: this only contains information regarding the started import, updates are pushed through the websocket. + +``` +{ + "watch": { + "exchange": "poloniex", + "currency": "USDT", + "asset": "BTC" + }, + "id": "7324830525419355", + "latest": "", + "from": "2018-04-03 05:46", + "to": "2018-07-03 05:46" +} +``` + +### POST /api/startGekko + +Start a gekko instance + +**request** + +*(a valid gekko config object)* + +In order to start a stratRunner, pass a config object that includes plugins required for a stratrunner (such as paperTrader, tradingAdvisor and performanceAnalyzer). + +``` +{ + "watch": { + "exchange": "poloniex", + "currency": "USDT", + "asset": "BTC" + }, + "candleWriter": { + "enabled": true, + }, + "type": "market watcher", + "mode": "realtime" +} +``` + +**response** + +NOTE: this only contains information regarding the started gekko, updates from are pushed through the websocket. + +``` +{ + "mode": "realtime", + "config": { + // config that was passed in the request + }, + "id": "2018-07-03-13-49-watcher-404457189518044", + "type": "watcher", + "logType": "watcher", + "active": true, + "stopped": false, + "errored": false, + "errorMessage": false, + "events": { + "initial": {}, + "latest": {} + }, + "start": "2018-07-03T05:49:06.685Z" +} +``` + +### POST /api/stopGekko + +Stop a gekko instance + +**request** + +``` +{ + "id": "2018-07-03-13-49-watcher-404457189518044" +} +``` + +**response** + +``` +{ + "status": "ok" +} +``` + +### POST /api/deleteGekko + +Delete a stopped gekko instance from the archive. + +**request** + +``` +{ + "id": "2018-07-03-13-49-watcher-404457189518044" +} +``` + +**response** + +``` +{ + "status": "ok" +} +``` + +### POST /api/getCandles + +Load candles directly from the database. + +**request** + +``` +{ + "watch": { + "exchange": "poloniex", + "currency": "USDT", + "asset": "BTC" + }, + "daterange": { + "to": "2018-07-03T05:53:00.000Z", + "from": "2018-07-03T05:40:00.000Z" + }, + "candleSize": 1 +} +``` + +**response** + +``` +[ + { + "start": 1530596400, + "open": 6645.49999958, + "high": 6649.83970225, + "low": 6642, + "close": 6642, + "vwp": 6644.840723040122, + "volume": 0.007383000000000001, + "trades": 23 + }, + { + "start": 1530596460, + "open": 6649.69979936, + "high": 6649.69979936, + "low": 6642, + "close": 6648.89999912, + "vwp": 6645.926390922748, + "volume": 0.007383450000000001, + "trades": 21 + }, + // etc. +] +``` + +## Websocket API + +A websocket server is started at the basepath of the API. Connecting to it means you'll receive update messages. Messages you send to the server via the websocket connection are completely ignored, to control the server use the REST API instead. + +The possible messages are: + +### import_update + +Whenever there is an update regarding a running import. + +``` +{ + type: 'import_update', + import_id: '7324830525419355', + updates: { + latest: '2018-07-03T05:49:06.685Z', + done: false + } +} +``` + +### import_error + +Whenever there is a fatal error in a running import. After receiving this the import will not continue. + +``` +{ + type: 'import_error', + import_id: '7324830525419355', + error: "Error string" +} +``` + +### gekko_new + +Whenever a new Gekko has been started. + +``` +{ + "type": "gekko_new", + "id": "2018-07-03-16-37-watcher-938815758693549", + "state": { + "mode": "realtime", + "config": { + // the complete config that was passed + }, + "id": "2018-07-03-16-37-watcher-938815758693549", + "type": "watcher", + "logType": "watcher", + "active": true, + "stopped": false, + "errored": false, + "errorMessage": false, + "events": { + "initial": {}, + "latest": {} + }, + "start": "2018-07-03T08:37:51.965Z" + } +} +``` + +### gekko_event + +Whenever a gekko instance has a new Gekko event. See the [gekko events doc](../events.md) for a list of all possible events and details. + +``` +{ + "type": "gekko_event", + "id": "2018-07-03-16-37-watcher-938815758693549", + "event": { + "type": "candle", + "payload": { + "start": "2018-07-03T08:38:00.000Z", + "open": 6623.00000066, + "high": 6623.00000066, + "low": 6623.00000066, + "close": 6623.00000066, + "vwp": 6623.00000066, + "volume": 0, + "trades": 0 + } + } +} +``` + +### gekko_stopped + +Whenever a Gekko has been stopped, does not fire when the instance had a fatal error. For that see `gekko_error`. + +``` +{ + "type": "gekko_stopped", + "id": "2018-07-03-16-37-watcher-938815758693549" +} +``` + +### gekko_error + + +Whenever a gekko received a fatal error and crashed. + +``` +{ + "type": "gekko_error", + "id": "2018-07-03-16-37-watcher-938815758693549", + "error": "Error message" +} +``` + + +### gekko_archived + +Whenever the server does not consider the instance to be relevant anymore, fires after a gekko has been stopped or had a fatal error. After this event the gekko instance is placed in an in memory archive. It will stay here stale (as it won't be updated) until it's deleted. + +``` +{ + "type": "gekko_stopped", + "id": "2018-07-03-16-37-watcher-938815758693549" +} +``` + +### gekko_deleted + +Whenever an archived gekko instance got deleted. + +``` +{ + "type": "gekko_stopped", + "id": "2018-07-03-16-37-watcher-938815758693549" +} +``` diff --git a/docs/introduction/about_gekko.md b/docs/introduction/about_gekko.md new file mode 100644 index 0000000..168b6c6 --- /dev/null +++ b/docs/introduction/about_gekko.md @@ -0,0 +1,80 @@ +# About Gekko + +Gekko is a **free and open source** Bitcoin TA trading and backtesting platform that connects to popular Bitcoin exchanges. It is written in javascript and runs on [nodejs](http://nodejs.org). + +*Use Gekko at your own risk.* + +![screen shot of gekko backtesting](https://user-images.githubusercontent.com/969743/35054500-fc705b46-fbac-11e7-9652-306c468505a3.png) + +## The gist + +Gekko is a tool that makes it very easy to automate your own trading strategies. + +![gist of gekko](https://gekko.wizb.it/_static/gekko-gist.png) + +You can either create your own trading strategy or start with the built-in example strategies. Once you have a strategy you can use Gekko to automatically run it in a few different ways: + +- **Backtest**: You can start a simulation of the strategy over a historical data period and Gekko will tell you what would have happened (which trades would have been performed as well as overall performance and risk metrics). +- **Paper trader**: You can run the strategy in realtime simulate trading (trade with fake money) to see in realtime how profitable your strategy would have been. +- **Tradebot**: You can run the strategy in realtime and automatically execute orders based on the signals. + +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?* + +## Automated Trading platform + +Gekko can watch the realtime markets, automatically executing and evaluating your strategies in the process. Whilst doing this Gekko will store all market data it sees, this makes it possible to later simulate trading strategies against historical data to see whether they would have been profitable (backtesting). + +## Limitations + +Gekko is not built for HFT or anything related to being the fastest (like arbitrage) as well as some other things. Please see the [scope page](./scope.md) to read more about what you can and cannot do with Gekko. + +## How does Gekko work? + +![Gekko architecture](https://wizb.it/gekko/static/architecture.jpg) + +Read more in the [architecture documentation](../internals/architecture.md). + +## Credits + +* The title is inspired by [Bateman](https://github.com/fearofcode/bateman). +* This project is inspired by the [GoxTradingBot](https://github.com/virtimus/GoxTradingBot/) Chrome plugin (which in turn is inspired by [Goomboo's journal](https://bitcointalk.org/index.php?topic=60501.0)). + +## Final + +If Gekko helped you in any way, you can always leave me a tip at (BTC) 13r1jyivitShUiv9FJvjLH7Nh1ZZptumwW + +## Disclaimer + +Gekko (nor anyone behind this project) DOES NOT give investment advice. All advice seen within Gekko is the result of running YOUR OWN strategies against the market. On top of that there might be bugs in the code - Gekko DOES NOT come with ANY warranty. + +## License + +The MIT License (MIT) + +Copyright (c) 2014 Mike van Rossum + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/docs/introduction/getting_help.md b/docs/introduction/getting_help.md new file mode 100644 index 0000000..379bfbd --- /dev/null +++ b/docs/introduction/getting_help.md @@ -0,0 +1,25 @@ +# Getting Help + +Are you getting stuck installing or using Gekko? Are you struggling with understanding Gekko? Running into problems creating your own plugin or strategy? Do you want to discuss awesome things you want to do with Gekko? This page will explain what to do! + +## Online community + +If you have trouble with anything specific or you want to provide any type of feedback the online community is the place to be. You can find the community at: + +1. [The online webforum](https://forum.gekko.wizb.it) +2. [The discord channel](https://discord.gg/26wMygt) + +These communities are the place for things like: + +- Solving installation problems + - Solving installation errors + - Helping set up Gekko on a server or embedded device +- Requesting/requesting new features and or changes such as: + - New exchanges to add + - New plugins to add + - General new features +- Getting clarity on what Gekko can do and how it works + +## Technical discussion + +The links above are always the first place to go to when discussing new features. But if you've found a clear bug or you want to **keep the complete discussion 100% technical (meaning completely code and architecture related)** you can use the [bug tracker on github](https://github.com/askmike/gekko/issues). Note that we are trying to keep all other discussions out of the bug tracker to keep the whole thing manageable :) diff --git a/docs/introduction/roadmap.md b/docs/introduction/roadmap.md new file mode 100644 index 0000000..95e86d4 --- /dev/null +++ b/docs/introduction/roadmap.md @@ -0,0 +1,12 @@ +# Roadmap + +Gekko is ever evolving and we are constantly working on new features and upgrades! + +This is a small selection of things currently on the roadmap: + +## Current TODO + +- Improve stability of new event and backtest engine +- Port the old supported exchanges to Gekko Broker +- new UI, see [here](https://forum.gekko.wizb.it/thread-1429-post-58996.html) +- advanced orders from your strategy (Take Profit and Stop Loss) \ No newline at end of file diff --git a/docs/introduction/scope.md b/docs/introduction/scope.md new file mode 100644 index 0000000..3ca4751 --- /dev/null +++ b/docs/introduction/scope.md @@ -0,0 +1,30 @@ +# Scope + +Gekko is a free and open source tool that is designed as a starters kit for automated trading on cryptocurrency markets. Gekko aims to have a low barrier entry to writing your own strategies (however a basic scripting knowledge in javascript is required for users who create their own strategies). This page will discuss the scope of some components of Gekko, explaining how things are done and why they are done a certain way. + +## Market data + +Gekko aggregates all market data into minutely candles (OHLC, VWP and amount of trades). This means Gekko only has to store candles on disk, which take up a predictable amount of space on your harddrive. The tradebot will use some additional market data (the orderbook) to execute orders efficiently, but this data is not visible anywhere else. + +## Strategies + +Strategies are simple scripts that handle new market data (OHLC candles) as well as calculated indicator results (strategies specify what indicators they want with which settings). Every time there is new data the strategy can determine to signal either LONG or SHORT. That's about it! This very simple design (candles + indicator values go in => signals come out) is quite powerful. + +Unfortunately this simple design can sometimes be limiting, here are some limitations: + +- Since strategies are only fed candles (and indicator values): + - Strategies cannot act on a smaller timeframe than 1 minute. + - Strategies cannot see any trades. + - Strategies cannot look at the orderbook. +- Strategies do not know what the current portfolio looks like. +- Strategies can only trigger a LONG or SHORT which signals to go "all in". + +## Execution strategy + +When you are using Gekko for a real tradebot Gekko will create orders at the exchange whenever your strategy signals an advice (long or short). If your strategy signal a long advice Gekko will try to buy as much "asset" as it can get with all your "currency" (if your strat is running on USD/BTC that would mean buying BTC with all your USD). As for creating the orders Gekko is conservative and stays on your side of the orderbook (this means you don not lose on the spread, slippage or taker fees). An example: + +If your strategy signals a LONG signal and this is the current orderbook: + +screen shot 2017-08-26 at 23 26 22 + +Gekko will try to buy bitcoin by placing a limit order at a price of `4336.29` in the hope someone sells into the order. Every few seconds Gekko will readjust the order to stay on top of the orderbook. *Note that order simulation (in the paper trader and backtester) is handled differently, since the orderbook is not used in the simulation.* diff --git a/docs/introduction/supported_exchanges.md b/docs/introduction/supported_exchanges.md new file mode 100644 index 0000000..37bdb50 --- /dev/null +++ b/docs/introduction/supported_exchanges.md @@ -0,0 +1,66 @@ +## Supported exchanges + +Gekko is able to directly communicate with the APIs of a number of exchanges. However communication with some exchanges is somewhat limited. Gekko makes the distinction between the following features: + +- Monitoring: Gekko is able to retrieve live market data from the exchange. Gekko can store and run trading simulations over this data. +- Live Trading: Gekko is able to automatically execute orders (based on the signals of your strategy). This turns Gekko into a trading bot. +- Importing: Gekko is able to retrieve historical market data. This way you can easily get a month of market data over which you can [backtest][1] your strategy. + +| Exchange | Monitoring | Live Trading | Importing | Notes | +| --------------------- |:----------:|:------------:|:---------:| ------------------------- | +| [Binance][24] | ✓ | ✓ | ✓ | | +| [Poloniex][2] | ✓ | ✓ | ✓ | | +| [GDAX][3] | ✓ | ✓ | ✓ | | +| [BTCC][4]* | ✓ | ✓ | ✓ | (=BTCChina) | +| [Bitstamp][5]* | ✓ | ✓ | ✕ | | +| [Kraken][6] | ✓ | ✓ | ✓ | | +| [Bitfinex][7] | ✓ | ✓ | ✓ | | +| [Bittrex][8] | ✓ | ✕ | ✕ | API problems ([#2310][26])| +| [coinfalcon][25] | ✓ | ✓ | ✓ | | +| [The Rock Trading][28]| ✓ | ✓ | ✓ | | +| [EXMO][27] | ✓ | ✓ | ✕ | | +| [wex.nz][9]* | ✓ | ✓ | ✕ | | +| [Gemini][10]* | ✓ | ✓ | ✕ | | +| [Okcoin.cn][11]* | ✓ | ✓ | ✕ | China, see [#352][20] | +| [Cex.io][12]* | ✓ | ✕ | ✕ | | +| [BTC Markets][13]* | ✓ | ✓ | ✕ | | +| [Luno][14] | ✓ | ✓ | ✓ | previously BitX | +| [lakeBTC][15]* | ✓ | ✕ | ✕ | | +| [meXBT][16]* | ✓ | ✕ | ✕ | see [here][21] | +| [zaif][17]* | ✓ | ✕ | ✕ | | +| [lakeBTC][18]* | ✓ | ✕ | ✕ | | +| [bx.in.th][19]* | ✓ | ✕ | ✕ | | +| [bitcoin.co.id][22]* | ✓ | ✓ | ✕ | | +| [Quadriga CX][23]* | x | x | ✕ | Exchange is down. | | + + +*Temporary disabled since 0.6! If you were planning on using this exchange please e-mail me (address at the bottom of this page). + +[1]: ../features/backtesting.md +[2]: https://poloniex.com +[3]: https://gdax.com +[4]: https://btcc.com +[5]: https://bitstamp.com +[6]: https://kraken.com +[7]: https://bitfinex.com +[8]: https://bittrex.com +[9]: https://wex.nz +[10]: https://gemini.com +[11]: https://www.okcoin.cn +[12]: https://cex.io +[13]: https://btcmarkets.net +[14]: https://www.luno.com +[15]: https://lakebtc.com +[16]: https://mexbt.com +[17]: https://zaif.jp/trade_btc_jpy +[18]: https://lakebtc.com +[19]: https://bx.in.th +[20]: https://github.com/askmike/gekko/pull/352 +[21]: https://github.com/askmike/gekko/issues/288#issuecomment-223810974 +[22]: https://vip.bitcoin.co.id/ +[23]: https://quadrigacx.com/ +[24]: https://www.binance.com/?ref=11236330 +[25]: https://coinfalcon.com/?ref=CFJSQBMXZZDS +[26]: https://github.com/askmike/gekko/pull/2310 +[27]: https://exmo.com +[28]: https://www.therocktrading.com/ diff --git a/docs/introduction/supporting_the_project.md b/docs/introduction/supporting_the_project.md new file mode 100644 index 0000000..a6c6860 --- /dev/null +++ b/docs/introduction/supporting_the_project.md @@ -0,0 +1,8 @@ +# Supporting the project + +Gekko is free and open source software. We don't make any money by publishing it, keeping it up to date or providing support. + +If Gekko helped you in any way (financially or otherwise) feel free to donate. If you want to see a specific feature implemented faster, feel free to [send an e-mail](mailto:gekko@mvr.me) to discuss. + +- Bitcoin tip jar: 13r1jyivitShUiv9FJvjLH7Nh1ZZptumwW +- Ethereum tip jar: 0x46e9249ADc30F5bfc6632e1d0b4286496d071Be7 \ No newline at end of file diff --git a/docs/strategies/creating_a_strategy.md b/docs/strategies/creating_a_strategy.md new file mode 100644 index 0000000..da8a6bb --- /dev/null +++ b/docs/strategies/creating_a_strategy.md @@ -0,0 +1,199 @@ +# Creating a strategy + +*Have you made your own strategy? Gekko Plus is hosting an official Strategy contest: submit your strategy and have a change to win 0.1 BTC! Read more details on [Gekko Plus](https://app.gekkoplus.com/contest).* + +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. If you want to understand how to create your own strategy you can watch this video or read the tech docs on this page. + +[![youtube video on how to create gekko strategies](https://gekko.wizb.it/_static/create-strat-vid.jpg)](https://www.youtube.com/watch?v=6-74ZhrG0BE) + +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. + +## Strategy boilerplate + + // Let's create our own strategy + var strat = {}; + + // Prepare everything our strat needs + strat.init = function() { + // your code! + } + + // 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) { + // your code! + } + + // Optional for executing code + // after completion of a backtest. + // This block will not execute in + // live use as a live gekko is + // never ending. + strat.end = function() { + // your code! + } + + module.exports = strat; + +# Strategy lifecycle methods + +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. Your strategy can initialize state and [register indicators](#Indicators). + +### update function + +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 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 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 + +If you find out in the check function that you want to give new advice to the trader you can use the advice function: + + this.advice({ + direction: 'long', // or short + trigger: { // ignored when direction is not "long" + type: 'trailingStop', + trailPercentage: 5 + // or: + // trailValue: 100 + } + }); + +The trigger is optional, if the direction is long and the trigger is specified as a trailingStop this will request the trader to create a trail stop trigger. + +### candle variable + +The following list of candle variables will be available when writing strategies, they are part of the candle object which is given to your `update` and `check` functions (it's also accessable through `this.candle`). + + - candle.close: the closing price of the candle + - candle.high: the highest price of the candle + - candle.low: the lowest price of the candle + - candle.volume: the trading volume of that candle + - candle.trades: number of trades in that candle + +## 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. +- It is advised to set history `config.tradingAdvisor.historySize` the same as the requiredHistory as Gekko will use this property to create an initial batch of candles. +- Never rely on anything based on system time because each method can run on live markets as well as during backtesting. You can look at the `candle.start` property which is a `moment` object of the time the candles started. + +## Strategy tools + +To help you Gekko has a number of strategy tools. + +### Indicators + +Gekko supports a few indicators natively, in addition to the integration of indicators from the library [TA-lib](http://ta-lib.org/). + +#### Example usage + +If you want to use an indicator you can add it in the `init` function and Gekko will handle the updating for you on every candle (before the update and before the check call): + + // add a native indicator + this.addIndicator('name', 'type', parameters); + +or + + // add a TA-lib indicator + this.addTalibIndicator('name', 'type', parameters); + +or + + // add a Tulip indicator + this.addTulipIndicator('name', 'type', parameters); + +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 function: + + // add a native indicator + var parameters = {short: 10, long: 20, signal: 9}; + this.addIndicator('mynativemacd', 'MACD', parameters); + + // add a TA-lib indicator + var parameters = {optInFastPeriod: 10, optInSlowPeriod: 21, optInSignalPeriod: 9}; + this.addTalibIndicator('mytalibmacd', 'macd', parameters); + + // add a Tulip indicator + var parameters = {optInFastPeriod: 10, optInSlowPeriod: 21, optInSignalPeriod: 9}; + this.addTulipIndicator('mytulipmacd', 'macd', parameters); + +In your check or update function: + + var result = this.indicators.mytalibmacd.result; + +See the [TA-lib indicators](./talib_indicators.md) document for a list of all supported TA-lib indicators and their required parameters. + +See the [Tulip indicators](./tulip_indicators.md) document for a list of all supported Tulip indicators and their required parameters. + +### Strategy parameters + +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 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___ + +### 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 other code + var _ = require('lodash'); + var async = require('async'); + +### Logging + +Gekko has a small logger you can use (preferably in your log function): + + // before any other code + var log = require('../core/log.js'); + + // 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 strategies and want to share it with the world feel free to contribute it to gekko. diff --git a/docs/strategies/gekko_indicators.md b/docs/strategies/gekko_indicators.md new file mode 100644 index 0000000..0fcecaf --- /dev/null +++ b/docs/strategies/gekko_indicators.md @@ -0,0 +1,122 @@ +# Gekko indicators + +When [creating your own strategy](./creating_a_strategy.md) there are a few built in indicators you can use that ship with Gekko. + +## Example + +If you want to use the MACD indicator from Gekko, you need to register it in your strategy like so: + + method.init = function() { + var settings = { + short: 10, + long: 21, + signal: 9 + }; + + // add the indicator to the strategy + this.addIndicator('mymacd', 'MACD', settings); + } + + method.check = function() { + // use indicator results + var macdiff = this.indicators.mymacd.result; + + // do something with macdiff + } + +## Indicators + +Here is a list of all supported indicators, click on them to read more about what they are and how to implement them in Gekko: + +- [EMA](#EMA) +- [PPO](#PPO) +- [CCI](#CCI) +- [DEMA](#DEMA) +- [LRC](#LRC) +- [MACD](#MACD) +- [RSI](#RSI) +- [SMA](#SMA) +- [TSI](#TSI) +- [UO](#UO) + +### EMA + +> **What is an 'Exponential Moving Average - EMA'** +> "An exponential moving average (EMA) is a type of moving average that is similar to a simple moving average, except that more weight is given to the latest data. It's also known as the exponentially weighted moving average. This type of moving average reacts faster to recent price changes than a simple moving average." + +*[More info on investopedia](http://www.investopedia.com/terms/e/ema.asp).* + +You can implement the EMA like so: + + method.init = function() { + var weight = 10; + + // add the indicator to the strategy + this.addIndicator('myema', 'EMA', weight); + } + + method.check = function() { + // use indicator results + var ema = this.indicators.myema.result; + + // do something with macdiff + } + + +### PPO +> **What is the 'Percentage Price Oscillator - PPO'** +> "The percentage price oscillator (PPO) is a technical momentum indicator showing the relationship between two moving averages." + +*[More info on investopedia](https://www.investopedia.com/terms/p/ppo.asp).* + +### CCI +> **What is the 'Commodity Channel Index - CCI'** +> "The Commodity Channel Index​ (CCI) is a momentum based technical trading tool used most often to help determine when an investment vehicle is reaching a condition of being overbought or oversold." + +*[More info on investopedia](https://www.investopedia.com/terms/c/commoditychannelindex.asp).* + +### DEMA + +> **What is the 'Double Exponential Moving Average - DEMA'** +> "The DEMA is a fast-acting moving average that is more responsive to market changes than a traditional moving average. It was developed in an attempt to create a calculation that eliminated some of the lag associated with traditional moving averages." + +*[More info on investopedia](https://www.investopedia.com/terms/d/double-exponential-moving-average.asp).* + +### LRC + +> **What is the 'Linear Regression Channel - LRC'** +> "This indicator plots a line that best fits the prices specified over a user-defined time period. The Linear Regression Curve is used mainly to identify trend direction and is sometimes used to generate buy and sell signals." + +*[More info on Interactive Brokers](https://www.interactivebrokers.co.uk/en/software/tws/usersguidebook/technicalanalytics/linearregressioncurve.htm).* + +### MACD +> **What is the 'Moving Average Convergence Divergence - MACD'** +> "Moving average convergence divergence (MACD) is a trend-following momentum indicator that shows the relationship between two moving averages of prices." + +*[More info on investopedia](https://www.investopedia.com/terms/m/macd.asp).* + +### RSI +> **What is the 'Relative Strength Index - RSI'** +> "Compares the magnitude of recent gains and losses over a specified time period to measure speed and change of price movements of a security. It is primarily used to attempt to identify overbought or oversold conditions in the trading of an asset." + +*[More info on investopedia](https://www.investopedia.com/terms/r/rsi.asp).* + +### SMA +> **What is the 'Simple Moving Average - SMA'** +> "A simple moving average (SMA) is an arithmetic moving average calculated by adding the closing price of the security for a number of time periods and then dividing this total by the number of time periods." + +*[More info on investopedia](https://www.investopedia.com/terms/s/sma.asp).* + +### TSI + +> **What is the 'True Strength Index - TSI'** +> "A technical momentum indicator that helps traders determine overbought and oversold conditions of a security by incorporating the short-term purchasing momentum of the market with the lagging benefits of moving averages." + +*[More info on investopedia](https://www.investopedia.com/terms/t/tsi.asp).* + +### UO + +> **What is the 'Ultimate Oscillator - UO'** +> "A technical indicator that uses the weighted average of three different time periods to reduce the volatility and false transaction signals that are associated with many other indicators that mainly rely on a single time period" + +*[More info on investopedia](https://www.investopedia.com/terms/u/ultimateoscillator.asp).* diff --git a/docs/strategies/introduction.md b/docs/strategies/introduction.md new file mode 100644 index 0000000..6d66840 --- /dev/null +++ b/docs/strategies/introduction.md @@ -0,0 +1,193 @@ +# Gekko Strategies + +Gekko uses [technical analysis indicators](http://www.investopedia.com/articles/active-trading/102914/technical-analysis-strategies-beginners.asp) inside **strategies**. + +This investment advice is going to be either **long** or **short**. Long indicates that Gekko the asset should be bought and short indicates that it should be sold. + +If you are familiar with javascript you can easily create your own strategies. Here is a video explaining everything you need to know: + +[![youtube video on how to create gekko strategies](https://gekko.wizb.it/_static/create-strat-vid.jpg)](https://www.youtube.com/watch?v=6-74ZhrG0BE) + +Below you can find simple and exemplary strategies that come with Gekko. These strategies come with Gekko and serve as examples. + +Gekko currently comes with the following example strategies: + + - [DEMA](#DEMA) + - [MACD](#MACD) + - [PPO](#PPO) + - [RSI](#RSI) + - [StochRSI](#StochRSI) + - [CCI](#CCI) + - [talib-macd](#talib-macd) + - [tulip-macd](#tulip-macd) + +### DEMA + +This strategy uses `Exponential Moving Average crossovers` to determine the current trend the +market is in. Using this information it will suggest to ride the trend. Note that this is +not MACD because it just checks whether the longEMA and shortEMA are [threshold]% removed +from each other. + +This strategy is fairly popular in bitcoin trading due to Bitcointalk user Goomboo. Read more about this strategy in [his topic](https://bitcointalk.org/index.php?topic=60501.0) or [here](http://stockcharts.com/school/doku.php?id=chart_school:technical_indicators:moving_averages). + +You can configure the following parameters: + + # EMA weight (α) + # the higher the weight, the more smooth (and delayed) the line + short = 10 + long = 21 + + [thresholds] + down = -0.025 + up = 0.025 + +- short is the short EMA that moves closer to the real market (including noise) +- long is the long EMA that lags behind the market more but is also more resistant to noise. +- the down threshold and the up threshold tell Gekko how big the difference in the lines needs to be for it to be considered a trend. If you set these to 0 each line cross would trigger new advice. + +### MACD + +The MACD is one of the most popular trend watching **indicators** in finance, it was created by Gerald Appel in the late 1970s. By using multiple price averages (EMAs) of different periods (one that follows the market more closely and one that lags behind, only catching bigger price swings). The indicator itself ouputs multiple numbers, when comparing them they can be interepted as signals that show when the trend of the price is changing. + +The MACD **strategy** in Gekko is a simple strategy that implements the indicator. By comparing the difference between the EMAs from the signal the strategy triggers buy and sell signals. The strategy does come with additional logic: + +- **thresholds**: a signal will be triggered as soon as the difference goes above or below a configurable threshold, making the strategy more flexible than simply checking if the difference is above or below zero. If you set these to 0 each line cross would trigger new advice. +- **persistance**: instead of trading as soon the difference is above or below the threshold, the strategy will wait a few candles to see if the difference keeps persisting. Only if it does the strategy will actually signal to Gekko to buy or sell. By setting persistance to 0 this behaviour is disabled. +- **short** is the short EMA that moves closer to the real market (including noise) +- **long** is the long EMA that lags behind the market more but is also more resistant to noise. +- **signal** is the EMA weight calculated over the difference from short/long. + +Read more about it [here](http://stockcharts.com/school/doku.php?id=chart_school:technical_indicators:moving_average_convergence_divergence_macd). + +You can configure the following parameters: + + # EMA weight (α) + # the higher the weight, the more smooth (and delayed) the line + short = 10 + long = 21 + signal = 9 + + # the difference between the EMAs (to act as triggers) + [thresholds] + down = -0.025 + up = 0.025 + # How many candle intervals should a trend persist + # before we consider it real? + persistence = 1 + +### PPO + +Very similar to MACD but also a little different, read more [here](http://stockcharts.com/school/doku.php?id=chart_school:technical_indicators:price_oscillators_ppo). + +You can configure the following parameters: + + # EMA weight (α) + # the higher the weight, the more smooth (and delayed) the line + short = 12 + long = 26 + signal = 9 + + # the difference between the EMAs (to act as triggers) + [thresholds] + down = -0.025 + up = 0.025 + # How many candle intervals should a trend persist + # before we consider it real? + persistence = 2 + +- short is the short EMA that moves closer to the real market (including noise) +- long is the long EMA that lags behind the market more but is also more resistant to noise. +- signal is the EMA weight calculated over the difference from short/long. +- the down threshold and the up threshold tell Gekko how big the difference in the lines needs to be for it to be considered a trend. If you set these to 0 each line cross would trigger new advice. +- persistence tells Gekko how long the thresholds needs to be met until Gekko considers the trend to be valid. + +### RSI + +The RSI indicator is and old but ever popular trend watching **indicator**, introduced in 1978 by J. Welles Wilder this simple indicator has never stopped to be a popular tool for traders. In its essence RSI follows a simple formula to measure the speed by which the price is changing. When the price keeps going up at an accelarating rate the market might be overbought and a reversal might come next. + +The RSI **strategy** in Gekko is a simple strategy that implements the RSI indicator. By calculating the RSI as the market develops this strategy can trigger buy or sell signals based on the RSI going too high (overbought) or too low (oversold). The strategy does come with additional logic: + +- **interval** is the amount of periods the RSI should use. +- **thresholds** determine what level of RSI would trigger an up or downtrend. +- **persistance**: instead of trading as soon the difference is above or below the threshold, the strategy will wait a few candles to see if the difference keeps persisting. Only if it does the strategy will actually signal to Gekko to buy or sell. By setting persistance to 0 this behaviour is disabled. + +Read more about it [here](http://stockcharts.com/school/doku.php?id=chart_school:technical_indicators:relative_strength_index_rsi). + +You can configure the following parameters: + + interval = 14 + + [thresholds] + low = 30 + high = 70 + # How many candle intervals should a trend persist + # before we consider it real? + persistence = 2 + +### StochRSI + +The StochRSI indicator uses an RSI indicator at its core and as such is similar to the RSI strategy that uses this indicator. The difference is that after the RSI is calculated a stochastic oscillator is calculated over the resulting RSI values. When a market is trending upwards for a long time the RSI values tend to go and stay high. The stochastic oscillator will compare the RSI values from the last period with eachother. This results in the StochRSI indicator therefor indicate how high or low the RSI values have been historically over the last n periods. + +The StochRSI **strategy** in Gekko is a simple strategy that implements the StochRSI indicator. By calculating the StochRSI as the market develops this strategy can trigger buy or sell signals based on the signal going too high (overbought) or too low (oversold). The strategy does come with configurables: + +- **interval**: This setting defines both the RSI interval used to calculate the RSI values as well as the historical period used by the stochastic oscillator to compare RSI values with. +- **persistance**: instead of trading as soon the difference is above or below the threshold, the strategy will wait a few candles to see if the difference keeps persisting. Only if it does the strategy will actually signal to Gekko to buy or sell. By setting persistance to 0 this behaviour is disabled. +- **thresholds**: The high and low thresholds are defined as numbers between 1 and 100. Different from StochRSI in other trading systems, this number indicates a percentage instead of a fraction of 1. + +You can configure the following parameters: + + interval = 3 + + [thresholds] + low = 20 + high = 80 + persistence = 3 + +### CCI + +You can configure the following parameters: + + # constant multiplier. 0.015 gets to around 70% fit + constant = 0.015 + + # history size, make same or smaller than history + history = 90 + + [thresholds] + up = 100 + down = -100 + persistence = 0 + +[TODO!] + +### talib-macd + +Similar to the default MACD strategy, this showcases how to add a TA-lib indicator to a strategy. + +You can configure the following parameters: + + [parameters] + optInFastPeriod = 10 + optInSlowPeriod = 21 + optInSignalPeriod = 9 + + [thresholds] + down = -0.025 + up = 0.025 + +### tulip-macd + +Similar to the default MACD strategy, this showcases how to add a Tulip indicator to a strategy. + +You can configure the following parameters: + + [parameters] + optInFastPeriod = 10 + optInSlowPeriod = 21 + optInSignalPeriod = 9 + + [thresholds] + down = -0.025 + up = 0.025 + +[TODO!] diff --git a/docs/strategies/talib_indicators.md b/docs/strategies/talib_indicators.md new file mode 100644 index 0000000..886d53b --- /dev/null +++ b/docs/strategies/talib_indicators.md @@ -0,0 +1,609 @@ +# TA-lib indicators + +When writing [your own strategy](./creating_a_strategy.md) you can use all indicators offered by [the TA-lib library](http://ta-lib.org/function.html). Gekko will pass the correct market data to TA-lib and you only have to provide the `optIn` configurable parameters. + +## Install + +### Bash on Windows, OSX or Linux + +Open your terminal. Then: + +``` +cd ~/gekko +npm install talib --no-save +``` + +## Example + +If you want to use the MACD indicator from TA-lib, you need to register it in your strategy like so: + + method.init = function() { + var customMACDSettings = { + optInFastPeriod: 10, + optInSlowPeriod: 21, + optInSignalPeriod: 9 + } + + // add the indicator to the strategy + this.addTalibIndicator('mymacd', 'macd', customMACDSettings); + } + + method.check = function() { + // use indicator results + var result = this.talibIndicators.mymacd.result; + var macddiff = result['outMACD'] - result['outMACDSignal']; + + // do something with macdiff + } + +## TA-lib Indicators + +Here is a list of all supported indicators, click on them to see the required parameters. + +- [ACCBANDS](#accbands) +- [AD](#ad) +- [ADOSC](#adosc) +- [ADX](#adx) +- [ADXR](#adxr) +- [APO](#apo) +- [AROON](#aroon) +- [AROONOSC](#aroonosc) +- [ATR](#atr) +- [AVGPRICE](#avgprice) +- [BBANDS](#bbands) +- [BOP](#bop) +- [CCI](#cci) +- [CMO](#cmo) +- [DEMA](#dema) +- [DX](#dx) +- [EMA](#ema) +- [HT_DCPERIOD](#ht_dcperiod) +- [HT_DCPHASE](#ht_dcphase) +- [HT_PHASOR](#ht_phasor) +- [HT_SINE](#ht_sine) +- [HT_TRENDLINE](#ht_trendline) +- [HT_TRENDMODE](#ht_trendmode) +- [IMI](#imi) +- [KAMA](#kama) +- [LINEARREG](#linearreg) +- [LINEARREG_ANGLE](#linearreg_angle) +- [LINEARREG_INTERCEPT](#linearreg_intercept) +- [LINEARREG_SLOPE](#linearreg_slope) +- [MA](#ma) +- [MACD](#macd) +- [MACDEXT](#macdext) +- [MACDFIX](#macdfix) +- [MAMA](#mama) +- [MAVP](#mavp) +- [MAX](#max) +- [MAXINDEX](#maxindex) +- [MEDPRICE](#medprice) +- [MFI](#mfi) +- [MIDPOINT](#midpoint) +- [MIDPRICE](#midprice) +- [MIN](#min) +- [MININDEX](#minindex) +- [MINMAX](#minmax) +- [MINMAXINDEX](#minmaxindex) +- [MINUS_DI](#minus_di) +- [MINUS_DM](#minus_dm) +- [MOM](#mom) +- [NATR](#natr) +- [OBV](#obv) +- [PLUS_DI](#plus_di) +- [PLUS_DM](#plus_dm) +- [PPO](#ppo) +- [ROC](#roc) +- [ROCP](#rocp) +- [ROCR](#rocr) +- [ROCR100](#rocr100) +- [RSI](#rsi) +- [SAR](#sar) +- [SAREXT](#sarext) +- [SMA](#sma) +- [STDDEV](#stddev) +- [STOCH](#stoch) +- [STOCHF](#stochf) +- [STOCHRSI](#stochrsi) +- [T3](#t3) +- [TEMA](#tema) +- [TRANGE](#trange) +- [TRIMA](#trima) +- [TRIX](#trix) +- [TSF](#tsf) +- [TYPPRICE](#typprice) +- [ULTOSC](#ultosc) +- [VARIANCE](#variance) +- [WCLPRICE](#wclprice) +- [WILLR](#willr) +- [WMA](#wma) + +## API + +### accbands + +Required parameters: + + - optInTimePeriod + +### ad + +Required parameters: + + - optInTimePeriod + +### adosc + +Required parameters: + + - optInFastPeriod + - optInSlowPeriod + +### adx + +Required parameters: + + - optInTimePeriod + +### adxr + +Required parameters: + + - optInTimePeriod + +### apo + +Required parameters: + + - optInFastPeriod + - optInSlowPeriod + - optInMAType + +### aroon + +Required parameters: + + - optInTimePeriod + +### aroonosc + +Required parameters: + + - optInTimePeriod + +### atr + +Required parameters: + + - optInTimePeriod + +### avgprice + +Required parameters: + + - optInTimePeriod + +### bbands + +Required parameters: + + - optInTimePeriod + - optInNbDevUp + - optInNbDevDn + - optInMAType + +### bop + +This indicator does not require any parameters. + +### cci + +Required parameters: + + - optInTimePeriod + +### cmo + +Required parameters: + + - optInTimePeriod + +### dema + +Required parameters: + + - optInTimePeriod + +### dx + +Required parameters: + + - optInTimePeriod + +### ema + +Required parameters: + + - optInTimePeriod + +### ht_dcperiod + +This indicator does not require any parameters. + +### ht_dcphase + +This indicator does not require any parameters. + +### ht_phasor + +This indicator does not require any parameters. + +### ht_sine + +This indicator does not require any parameters. + +### ht_trendline + +This indicator does not require any parameters. + +### ht_trendmode + +This indicator does not require any parameters. + +### imi + +Required parameters: + + - optInTimePeriod + +### kama + +Required parameters: + + - optInTimePeriod + +### linearreg + +Required parameters: + + - optInTimePeriod + +### linearreg_angle + +Required parameters: + + - optInTimePeriod + +### linearreg_intercept + +Required parameters: + + - optInTimePeriod + +### linearreg_slope + +Required parameters: + + - optInTimePeriod + +### ma + +Required parameters: + + - optInTimePeriod + - optInMAType + +### macd + +Required parameters: + + - optInFastPeriod + - optInSlowPeriod + - optInSignalPeriod + +### macdext + +Required parameters: + + - optInFastPeriod + - optInFastMAType + - optInSlowPeriod + - optInSlowMAType + - optInSignalPeriod + - optInSignalMAType + +### macdfix + +Required parameters: + + - SignalPeriod + +### mama + +Required parameters: + + - optInFastLimit + - optInSlowLimit + +### mavp + +Required parameters: + + - inPeriods + - optInMinPeriod + - optInMaxPeriod + - optInMAType + +### max + +Required parameters: + + - optInTimePeriod + +### maxindex + +Required parameters: + + - optInTimePeriod + +### medprice + +Required parameters: + + - optInTimePeriod + +### mfi + +Required parameters: + + - optInTimePeriod + +### midpoint + +Required parameters: + + - optInTimePeriod + +### midprice + +Required parameters: + + - optInTimePeriod + +### min + +Required parameters: + + - optInTimePeriod + +### minindex + +Required parameters: + + - optInTimePeriod + +### minmax + +Required parameters: + + - optInTimePeriod + +### minmaxindex + +Required parameters: + + - optInTimePeriod + +### minus_di + +Required parameters: + + - optInTimePeriod + +### minus_dm + +Required parameters: + + - optInTimePeriod + +### mom + +Required parameters: + + - optInTimePeriod + +### natr + +Required parameters: + + - optInTimePeriod + +### obv + +This indicator does not require any parameters. + +### plus_di + +Required parameters: + + - optInTimePeriod + +### plus_dm + +Required parameters: + + - optInTimePeriod + +### ppo + +Required parameters: + + - optInFastPeriod + - optInSlowPeriod + - optInMAType + +### roc + +Required parameters: + + - optInTimePeriod + +### rocp + +Required parameters: + + - optInTimePeriod + +### rocr + +Required parameters: + + - optInTimePeriod + +### rocr100 + +Required parameters: + + - optInTimePeriod + +### rsi + +Required parameters: + + - optInTimePeriod + +### sar + +Required parameters: + + - optInAcceleration + - optInMaximum + +### sarext + +Required parameters: + + - optInStartValue + - optInOffsetOnReverse + - optInAccelerationInitLong + - optInAccelerationLong + - optInAccelerationMaxLong + - optInAccelerationInitShort + - optInAccelerationShort + - optInAccelerationMaxShort + +### sma + +Required parameters: + + - optInTimePeriod + +### stddev + +Required parameters: + + - optInTimePeriod + - optInNbDev + +### stoch + +Required parameters: + + - optInFastK_Period + - optInSlowK_Period + - optInSlowK_MAType + - optInSlowD_Period + - optInSlowD_MAType + +### stochf + +Required parameters: + + - optInFastK_Period + - optInFastD_Period + - optInFastD_MAType + +### stochrsi + +Required parameters: + + - optInTimePeriod + - optInFastK_Period + - optInFastD_Period + - optInFastD_MAType + +### t3 + +Required parameters: + + - optInTimePeriod + - optInFastK_Period + - optInFastD_Period + - optInFastD_MAType + +### tema + +Required parameters: + + - optInTimePeriod + +### trange + +Required parameters: + + - optInTimePeriod + +### trima + +Required parameters: + + - optInTimePeriod + +### trix + +Required parameters: + + - optInTimePeriod + +### tsf + +Required parameters: + + - optInTimePeriod + +### typprice + +Required parameters: + + - optInTimePeriod + +### ultosc + +Required parameters: + + - optInTimePeriod + +### variance + +Required parameters: + + - optInTimePeriod + - optInNbDev + +### wclprice + +Required parameters: + + - optInTimePeriod + +### willr + +Required parameters: + + - optInTimePeriod + +### wma + +Required parameters: + + - optInTimePeriod diff --git a/docs/strategies/tulip_indicators.md b/docs/strategies/tulip_indicators.md new file mode 100644 index 0000000..c38e4a4 --- /dev/null +++ b/docs/strategies/tulip_indicators.md @@ -0,0 +1,709 @@ +# Tulip indicators + +When writing [your own strategy](./creating_a_trading_method.md) you can use all indicators offered by [the Tulip Indicators library](https://tulipindicators.org/). Gekko will pass the correct market data to Tulip and you only have to provide the `optIn` configurable parameters. + +## Install + +### Bash on Windows, OSX or Linux + +Open your terminal. Then: + +``` +cd ~/gekko +npm install tulind --no-save +``` + +## Example + +If you want to use the MACD indicator from Tulip, you need to register it in your strategy like so: + + method.init = function() { + var customMACDSettings = { + optInFastPeriod: 10, + optInSlowPeriod: 21, + optInSignalPeriod: 9 + } + + // add the indicator to the strategy + this.addTulipIndicator('mymacd', 'macd', customMACDSettings); + } + + method.check = function() { + // use indicator results + var result = this.tulipIndicators.mymacd.result; + var macddiff = result['macd'] - result['macdSignal']; + + // do something with macdiff + } + +## Tulip Indicators + +Here is a list of all supported indicators, click on them to see the required parameters. + +- [AD](#ad) +- [ADOSC](#adosc) +- [ADX](#adx) +- [ADXR](#adxr) +- [AO](#ao) +- [APO](#apo) +- [AROON](#aroon) +- [AROONOSC](#aroonosc) +- [ATR](#atr) +- [AVGPRICE](#avgprice) +- [BBANDS](#bbands) +- [BOP](#bop) +- [CCI](#cci) +- [CMO](#cmo) +- [CVI](#cvi) +- [DEMA](#dema) +- [DI](#di) +- [DM](#dm) +- [DPO](#dpo) +- [DX](#dx) +- [EMA](#ema) +- [EMV](#emv) +- [FISHER](#fisher) +- [FOSC](#fosc) +- [HMA](#hma) +- [KAMA](#kama) +- [KVO](#kvo) +- [LINREG](#linreg) +- [LINREGINTERCEPT](#linregintercept) +- [LINREGSLOPE](#linregslope) +- [MARKETFI](#marketfi) +- [MASS](#mass) +- [MEDPRICE](#medprice) +- [MFI](#mfi) +- [MSW](#msw) +- [NATR](#natr) +- [NVI](#nvi) +- [OBV](#obv) +- [PPO](#ppo) +- [PSAR](#psar) +- [PVI](#pvi) +- [QSTICK](#qstick) +- [ROC](#roc) +- [ROCR](#rocr) +- [RSI](#rsi) +- [SMA](#sma) +- [STOCH](#stoch) +- [SUM](#sum) +- [TEMA](#tema) +- [TR](#tr) +- [TRIMA](#trima) +- [TRIX](#trix) +- [TSF](#tsf) +- [TYPPRICE](#typprice) +- [ULTOSC](#ultosc) +- [VHF](#vhf) +- [VIDYA](#vidya) +- [VOLATILITY](#volatility) +- [VOSC](#vosc) +- [VWMA](#vwma) +- [WAD](#wad) +- [WCPRICE](#wcprice) +- [WILDERS](#wilders) +- [WILLR](#willr) +- [WMA](#wma) +- [ZLEMA](#zlema) + +## API + +### ad + +This indicator does not require any parameters. + +### adosc + +Required parameters: + + - optInFastPeriod + - optInSlowPeriod + +### adx + +More information on the [**Average Directonal Movement Index**](https://en.wikipedia.org/wiki/Average_directional_movement_index) + +Required parameters: + + - optInTimePeriod + +### adxr + +The **Average Directional Movement Index Rating** is used as a risk indicator for a security in an [adx](#adx) system. It evaluates the risk (volatility) of a security by smoothing the amplitude readings of the trend function of the adx. It is calculated as follows + + ADX plus the ADX (n-time periods ago) divided by 2 + +Higher readings conform to lower risk and lower readings conform to higher risk. +A investor wanting to lower the portfolio risk will therefore use securities with higher adxr ratings. + +Required parameters: + + - optInTimePeriod + +### ao + +More information about the [**Awesome Oscillator**](https://www.tradingview.com/wiki/Awesome_Oscillator_(AO)) + +This indicator does not require any parameters. + +### apo + +The **Absolute Price Oscillator** displays the difference between two exponential moving averages of a security's price and is expressed as an absolute value. It rates the trends strength in relation to the moving between the two moving averages with short-term momentum being the catalyst. + +Required parameters: + + - optInFastPeriod + - optInSlowPeriod + +### aroon + +More information about the [Aroon](https://www.tradingview.com/wiki/Aroon) Indicator. + +Required parameters: + + - optInTimePeriod + +### aroonosc + +The Aroon Oscillator belongs to the group of trend-following indicators and uses parts of the Aroon Indicator (namely "Aroon Up" and "Aroon Down") to indicate the strength of a current trend and the likelihood that it will continue. The Aroon Oscillator is calculated by subtracting Aroon Up from Aroon Down. Readings above zero indicate that an uptrend is present, while readings below zero indicate that a downtrend is present. + +Required parameters: + + - optInTimePeriod + +### atr + +More information about the [Average True Range](https://www.tradingview.com/wiki/Average_True_Range_(ATR)) + +Required parameters: + + - optInTimePeriod + +### avgprice + +The average price indicator calculates the mean of the open, high, low, and close of a bar + + open + high + low + close / 4 + +This indicator does not require any parameters. + +### bbands + +More information about [Bollinger Bands](https://www.tradingview.com/wiki/Bollinger_Bands_(BB)) + +Required parameters: + + - optInTimePeriod + - optInNbStdDevs + +### bop + +The Balance of Power indicator measures the market strength of buyers against sellers by assessing the ability of each side to drive prices to an extreme level. The calculation is: + + Balance of Power = (Close price – Open price) / (High price – Low price) + +The resulting value can be smoothed by a moving average. + +This indicator does not require any parameters. + +### cci + +More information about the [Commodity Channel Index](https://www.tradingview.com/wiki/Commodity_Channel_Index_(CCI)) + +Required parameters: + + - optInTimePeriod + +### cmo + +The Chande Momentum Oscillator is a technical momentum indicator. The indicator is created by calculating the difference between the sum of all recent higher closes and the sum of all recent lower closes and then dividing the result by the sum of all price movement over a given time period. The result is multiplied by 100 to give the -100 to +100 range. The defined time period is usually 20 periods. + + CMO = 100 * ((Sh - Sd)/ ( Sh + Sd ) ) + +Where: + +Sh = Sum of the difference between the current close and previous close on up days for the specified period. Up days are days when the current close is greater than the previous close. + +Sd = Sum of the absolute value of the difference between the current close and the previous close on down days for the specified period. Down days are days when the current close is less than the previous close. + +Required parameters: + + - optInTimePeriod + +### cvi + +More information about the [Chaikin Volatility Index](https://www.tradingview.com/wiki/Chaikin_Oscillator) also called the Chaikin Oscillator. + +Required parameters: + + - optInTimePeriod + +### dema + +More information about the [DEMA](https://en.wikipedia.org/wiki/Double_exponential_moving_average) + +Required parameters: + + - optInTimePeriod + +### di + +More information about the [Directional Indicator](https://en.wikipedia.org/wiki/Average_directional_movement_index) + +Required parameters: + + - optInTimePeriod + +### dm + +More information about the [Directional Movement Index](https://en.wikipedia.org/wiki/Average_directional_movement_index) + +Required parameters: + + - optInTimePeriod + +### dpo + +More information about the [Detrended Price Oscillator](https://en.wikipedia.org/wiki/Detrended_price_oscillator) + +Required parameters: + + - optInTimePeriod + +### dx + +More information about the [Directional Movement Index](https://en.wikipedia.org/wiki/Average_directional_movement_index) + +Required parameters: + + - optInTimePeriod + +### ema + +Exponential Moving Average (EMA) is similar to Simple Moving Average (SMA), measuring trend direction over a period of time. However, whereas SMA simply calculates an average of price data, EMA applies more weight to data that is more current. Because of its unique calculation, EMA will follow prices more closely than a corresponding SMA. + + EMA = (K x (C - P)) + P + +Where: + + - C = Current Price + - P = Previous periods EMA (A SMA is used for the first periods calculations) + - K = Exponential smoothing constant + +Required parameters: + + - optInTimePeriod + +### emv + +More information about the [Ease of Movement](https://www.tradingview.com/wiki/Ease_of_Movement_(EOM)) + +This indicator does not require any parameters. + +### fisher + +The Fisher Transform is a technical indicator that converts prices into a Gaussian normal distribution. The indicator enables traders to create a nearly Gaussian probability density function by normalizing prices. That is, the transformation makes peak swings relatively rare events and unambiguously identifies price reversals on a chart. + + Y = 0.5 * ln ((1+X)/(1-X)) + +Where: + + - "ln" denotes the abbreviated form of the natural logarithm + - "X" denotes the transformation of price to a level between -1 and 1 for ease of calculation + +Required parameters: + + - optInTimePeriod + +### fosc + +The forecast oscillator attempts to predict price action by comparing the results of a linear regression trendline to the actual price for that day. Positive values of the oscillator occur when the forecast price is above the actual price and negative values when the forecast price is below. If prices are consistently below the forecast, then a downturn in prices is likely. Conversely, if prices are consistently above the forecast then an upturn in prices is likely. + +Required parameters: + + - optInTimePeriod + +### hma + +Basic forms of moving averages like the Simple Moving Average lag price. The Exponential and Weighted Moving Averages were developed to address this lag by placing more emphasis on more recent data. The Hull Moving Average in contrast is an extremely fast and smooth moving average, it almost eliminates lag altogether and manages to improve smoothing at the same time. + + HMA= WMA(2*WMA(n/2) − WMA(n)),sqrt(n)) + +Required parameters: + + - optInTimePeriod + +### kama + +The Kaufman adaptive moving average belongs to the group of "intelligent" indicators. It´s concept addresses the fact that noisy markets should have a lagging indicator and trending markets should have a leading indicator. So it adapts itself to lag in sideways markets and lead in trending markets. + + KAMA(t) = KAMA(t-1) + sc(t) x (Price-KAMA(t-1)) + +Where: + + - KAMA(t) is the new adaptive moving average value + - KAMA(t-1) is the previous adaptive moving average value + - Price is the current price + - sc(t) is the smoothing constant + +Required parameters: + + - optInTimePeriod + +### kvo + +The Klinger Volume Oscillator measures trends of money flows based upon volume. The KO is derived from three types of data: the high-low price range, volume, and accumulation/distribution. Price range is a measure of movement and the force behind that movement is volume. Accumulation is when the sum of today's [high]+[low]+[close] is greater than yesterday's. Distribution is when today's sum is less than the yesterday's. When the sums are equal, the existing trend is maintained. + +Required parameters: + + - optInFastPeriod + - optInSlowPeriod + +### linreg + +The Linear Regression is a smoothing functions that works by preforming linear least squares regression over a moving window. It then uses the linear model to predict the value for the current bar. + +Required parameters: + + - optInTimePeriod + +### linregintercept + +More information about the [Linear Regression Intercept](https://en.wikipedia.org/wiki/Simple_linear_regression) + +Required parameters: + + - optInTimePeriod + +### linregslope + +More information about the [Linear Regression Intercept](https://en.wikipedia.org/wiki/Simple_linear_regression) + +Required parameters: + + - optInTimePeriod + +### macd + +More information about the [MACD](https://www.tradingview.com/wiki/MACD_(Moving_Average_Convergence/Divergence)) + +Required parameters: + + - optInFastPeriod + - optInSlowPeriod + - optInSignalPeriod + +### marketfi + +The Market Facilitation Index indicator combines price and volume in the analysis to establish the effectiveness of price movement by computing the price movement per volume unit. To calculate the Market Facilitation Index indicator the difference between the low and the high price are taken and divided by the volume. + +This indicator does not require any parameters. + +### mass + +The Mass Index indicator is used for finding trend reversals, based on the premise that reversals are likely to happen when the price range widens. It does not have a directional bias. The calculation, which compares the previous trading ranges (highs minus lows), uses Exponential Moving Averages (EMA). If there is substantial movement, the Mass Index Indicator increases if there is insubstantial movement the Mass Index Indicator decreases. + +Formula (usually a 9 period EMA is used) + + Single EMA = (x)-period exponential moving average (EMA) of the high-low differential + Double EMA = (y)-period EMA of the (x)-period EMA of the high-low differential + EMA Ratio = Single EMA divided by Double EMA + Mass Index = 25-period sum of the EMA Ratio + +Required parameters: + + - optInTimePeriod + +### medprice + +The median price indicator calculates the mean of the high and low for a bar. Despite the name, it does not calculate an actual median value. + + medprice(t) = (high(t) + low(t)) / 2 + +This indicator does not require any parameters. + +### mfi + +More information about the [MFI](https://www.tradingview.com/wiki/Money_Flow_(MFI)) + +Required parameters: + + - optInTimePeriod + +### msw + +The Maximum Entropy Spectrum Analysis indicator is a unique way to accurately measure short-term cycles in a market. It utilizes two sine plots to illustrate if the market is in a cycle mode or in a trend mode. When the two plots resemble a sine wave the market is in cycling mode. When the plots start to wander the market is in a trend mode. +The MESA Sine Wave indicator is a leading indicator that will anticipate cycle mode turning points rather than waiting for confirmation as is seen with most other oscillators. The indicator has the additional advantage that trend mode whipsaw signals are minimized. + +Required parameters: + + - optIn + +### natr + +The Normalized Average True Range is a measure of volatility. +It is calculated as follows: + + natr = (atr(t) / close(t)) * 100 + +Required parameters: + + - optInTimePeriod + +### nvi + +More information about the [NVI](https://en.wikipedia.org/wiki/Negative_volume_index) + +This indicator does not require any parameters. + +### obv + +More information about the [OBV](https://www.tradingview.com/wiki/On_Balance_Volume_(OBV)) + +This indicator does not require any parameters. + +### ppo + +More information about the [PPO](https://www.tradingview.com/wiki/Price_Oscillator_(PPO)) + +Required parameters: + + - optInFastPeriod + - optInSlowPeriod + +### psar + +More information about the [Parabolic SAR](https://www.tradingview.com/wiki/Parabolic_SAR_(SAR)) + +Required parameters: + + - optInAcceleration + - optInMaximum + +### pvi + +More information about the [PVI](https://en.wikipedia.org/wiki/Negative_volume_index) + +This indicator does not require any parameters. + +### qstick + +The Qstick Indicator identifies trends on a chart. It is calculated by taking an 'n' period moving average of the difference between the open and closing prices. A Qstick value greater than zero means that the majority of the last 'n' days have been up, indicating that buying pressure has been increasing. In summary, the measure provides an approximation for a security’s EMA, opening price, closing price, and their difference, as well as these values SMA. + +Required parameters: + + - optInTimePeriod + +### roc + +More information about the [Rate of Change](https://www.tradingview.com/wiki/Rate_of_Change_(ROC)) + +Required parameters: + + - optInTimePeriod + +### rocr + +Rate Of Change Ratio calculates the change between the current price and the price n bars ago. + +Required parameters: + + - optInTimePeriod + +### rsi + +More information about the [RSI](https://www.tradingview.com/wiki/Relative_Strength_Index_(RSI)) + +Required parameters: + + - optInTimePeriod + +### sma + +More information about the [SMA](https://www.tradingview.com/wiki/Moving_Average) + +Required parameters: + + - optInTimePeriod + +### stoch + +More information about the [STOCH](https://www.tradingview.com/wiki/Stochastic_(STOCH)) + +Required parameters: + + - optInFastKPeriod + - optInSlowKPeriod + - optInSlowDPeriod + +### sum + +The Sum Over Period indicator simply returns the sum of the last n bars. + +Required parameters: + + - optInTimePeriod + +### tema + +The Triple Exponential Moving Average is similar to the Exponential Moving Average or the Double Exponential Moving Average, but provides even less lag. Triple Exponential Moving Average is probably best viewed as an extension of Double Exponential Moving Average. + +It can be expressed in terms of the Exponential Moving Average as follows: + + tema = 3 * ema(in) - 3 * ema(ema(in)) + ema(ema(ema(in))) + +Required parameters: + + - optInTimePeriod + +### tr + +True range is a measure of volatility. It represents how much a security changed price on a given day. + +True range for each day is the greatest of: + + - Day's high minus day's low + - The absolute value of the day's high minus the previous day's close + - The absolute value of the day's low minus the previous day's close + +This indicator does not require any parameters. + +### trima + +The Triangular Moving Average is similar to the Simple Moving Average but instead places more weight on middle portion of the smoothing period and less weight on the newest and oldest bars in the period. +It is calculated for each bar as the weighted arithmetic mean of the previous n bars. For example, the weights w for an n of 4 are: 1, 2, 2, 1. The weights w for a n of 7 are: 1, 2, 3, 4, 3, 2, 1. It's easy to see why it's called the Triangular Moving Average. + +Required parameters: + + - optInTimePeriod + +### trix + +More information about the [TRIX](https://www.tradingview.com/wiki/TRIX) + +Required parameters: + + - optInTimePeriod + +### tsf + +The Time Series Forecast indicator displays the statistical trend of a security's price over a specified time period. The trend is based on linear regression analysis. Rather than plotting a straight linear regression trendline, the Time Series Forecast plots the last point of multiple linear regression trendlines. The resulting TSF indicator is sometimes referred to as the "moving linear regression" indicator or the "regression oscillator." + +Required parameters: + + - optInTimePeriod + +### typprice + +The Typical Price calculates the arithmetic mean of the high, low, and close of a bar. + +This indicator does not require any parameters. + +### ultosc + +More information about the [UO](https://www.tradingview.com/wiki/Ultimate_Oscillator_(UO)) + +Required parameters: + + - optInTimePeriod1 + - optInTimePeriod2 + - optInTimePeriod3 + +### vhf + +The Vertical Horizontal Filter determines whether prices are in a trending phase or a congestion phase. It is used to dertermine which other indicators are to be used in the current market trend. For example if the VHF suggests the market is in a range then a [mesa] could be used if the VHF suggests the market is in a range then one could use a [psar] to determine entry and exit points. + +Required parameters: + + - optInTimePeriod + +### vidya + +The Variable Index Dynamic Average indicator modifies the Exponential Moving Average by varying the smoothness based on recent volatility. + +Required parameters: + + - optInFastPeriod + - optInSlowPeriod + - optInAlpha + +### volatility + +The Annualized Historical Volatility indicator calculates the volatility over a moving window. + +Required parameters: + + - optInTimePeriod + +### vosc + +The Volume Oscillator identifies trends in volume using two moving averages of volume, one fast and one slow. The fast volume moving average is then subtracted from the slow moving average. + +Required parameters: + + - optInFastPeriod + - optInSlowPeriod + +### vwma + +The Volume Weighted Moving Average is simalair to a Simple Moving Average, but it weights each bar by its volume. + +Required parameters: + + - optInTimePeriod + +### wad + +Williams AD is a running sum of positive accumulation values (buying pressure) and negative distribution values (selling pressure), as determined by price's location within a given day's true range. The day's accumulation/distribution is then calculated by comparing today's closing price to yesterday's closing price. +To calculate the Williams' Accumulation/Distribution indicator, determine: + + True Range High (TRH) = Yesterday's close or today's high whichever is greater + True Range Low (TRL) = Yesterday's close or today's low whichever is less + +The Williams' Accumulation/Distribution indicator is a cumulative total of the daily values: + + - Williams A/D = Today's A/D + Yesterday's Williams A/D + +This indicator does not require any parameters. + +### wcprice + +The weighted close price indicator calculates the mean of the high, low, and close of a bar, but the close price is weighted to count for double. + +This indicator does not require any parameters. + +### wilders + +The Welles Wilder Smoothing indicator is basically the same as an [EMA](#ema) and can be used in the same manner. +It uses a different calculation but can be easily calculated by simply converting EMA values as follows: + + wilders = (ema + 1) / 2 + +Required parameters: + + - optInTimePeriod + +### willr + +More information about the [Williams R](https://www.tradingview.com/wiki/Williams_%25R_(%25R)) + +Required parameters: + + - optInTimePeriod + +### wma + +More information about the [WMA](https://en.wikipedia.org/wiki/Moving_average#Simple_moving_average) + +Required parameters: + + - optInTimePeriod + +### zlema + +Zero-Lag Exponential Moving Average modifies a Exponential Moving Average to greatly reduce lag. + +Required parameters: + + - optInTimePeriod diff --git a/exchange/.npmignore b/exchange/.npmignore new file mode 100644 index 0000000..2eca375 --- /dev/null +++ b/exchange/.npmignore @@ -0,0 +1 @@ +nusadua \ No newline at end of file diff --git a/exchange/README.md b/exchange/README.md new file mode 100644 index 0000000..6469cb1 --- /dev/null +++ b/exchange/README.md @@ -0,0 +1,3 @@ +# Gekko Broker + +see [the docs](https://gekko.wizb.it/docs/gekko-broker/introduction.html). \ No newline at end of file diff --git a/exchange/dependencyCheck.js b/exchange/dependencyCheck.js new file mode 100644 index 0000000..1cdcbc9 --- /dev/null +++ b/exchange/dependencyCheck.js @@ -0,0 +1,23 @@ +const deps = require('./package.json').dependencies; + +const missing = []; + +Object.keys(deps).forEach(dep => { + try { + require(dep); + } catch(e) { + if(e.code === 'MODULE_NOT_FOUND') { + missing.push(dep); + } + } +}); + +if(missing.length) { + console.error( + '\nThe following Gekko Broker dependencies are not installed: [', + missing.join(', '), + '].\n\nYou need to install them first, read here how:', + 'https://gekko.wizb.it/docs/installation/installing_gekko.html#Installing-Gekko-39-s-dependencies\n' + ); + process.exit(1); +} diff --git a/exchange/exchangeChecker.js b/exchange/exchangeChecker.js new file mode 100644 index 0000000..e717790 --- /dev/null +++ b/exchange/exchangeChecker.js @@ -0,0 +1,104 @@ +const _ = require('lodash'); +const fs = require('fs'); +const moment = require('moment'); +const errors = require('./exchangeErrors'); + +const Checker = function() { + _.bindAll(this); +} + +Checker.prototype.getExchangeCapabilities = function(slug) { + if(!fs.existsSync(__dirname + '/wrappers/' + slug + '.js')) + throw new errors.ExchangeError(`Gekko does not know the exchange "${slug}"`); + + return require('./wrappers/' + slug).getCapabilities(); +} + +// check if the exchange is configured correctly for monitoring +Checker.prototype.cantMonitor = function(conf) { + var slug = conf.exchange.toLowerCase(); + var exchange = this.getExchangeCapabilities(slug); + + if(!exchange) + return 'Gekko does not support the exchange ' + slug; + + var name = exchange.name; + + if('monitorError' in exchange) + return 'At this moment Gekko can\'t monitor ' + name + ', find out more info here:\n\n' + exchange.monitorError; + + var name = exchange.name; + + if(!_.includes(exchange.currencies, conf.currency)) + return 'Gekko only supports the currencies [ ' + exchange.currencies.join(', ') + ' ] at ' + name + ' (not ' + conf.currency + ')'; + + if(!_.includes(exchange.assets, conf.asset)) + return 'Gekko only supports the assets [ ' + exchange.assets.join(', ') + ' ] at ' + name + ' (not ' + conf.asset + ')'; + + var pair = _.find(exchange.markets, function(p) { + return p.pair[0] === conf.currency && p.pair[1] === conf.asset; + }); + + if(!pair) + return 'Gekko does not support this currency/assets pair at ' + name; + + // everything is okay + return false; +} + +// check if the exchange is configured correctly for fetching +// full history +Checker.prototype.cantFetchFullHistory = function(conf) { + var slug = conf.exchange.toLowerCase(); + var exchange = this.getExchangeCapabilities(slug); + + if(this.cantMonitor(conf)) + return this.cantMonitor(conf); + + var name = exchange.name; + + if(!exchange.providesFullHistory) + return 'The exchange ' + name + ' does not provide full history (or Gekko doesn\'t support importing it)'; + + if ("exchangeMaxHistoryAge" in exchange) { + if (moment(config.importer.daterange.from) < moment().subtract(exchange.exchangeMaxHistoryAge, "days")) { + return 'Unsupported date from! ' + exchange.name + ' supports history of max ' + exchange.exchangeMaxHistoryAge + ' days..'; + } + } +} + +// check if the exchange if configured correctly for real trading +Checker.prototype.cantTrade = function(conf) { + var cantMonitor = this.cantMonitor(conf); + if(cantMonitor) + return cantMonitor; + + var slug = conf.exchange.toLowerCase(); + var exchange = this.getExchangeCapabilities(slug); + var name = exchange.name; + + if(!exchange.tradable) + return 'At this moment Gekko can\'t trade at ' + name + '.'; + + if(conf.key === 'your-key') + return '"your-key" is not a valid API key'; + + if(conf.secret === 'your-secret') + return '"your-secret" is not a valid API secret'; + + var error = false; + _.each(exchange.requires, function(req) { + if(!conf[req]) + error = name + ' requires "' + req + '" to be set in the config'; + }, this); + + return error; +} + +Checker.prototype.settings = function(conf) { + var slug = conf.exchange.toLowerCase(); + return this.getExchangeCapabilities(slug); + +} + +module.exports = new Checker(); diff --git a/exchange/exchangeErrors.js b/exchange/exchangeErrors.js new file mode 100644 index 0000000..4dca77c --- /dev/null +++ b/exchange/exchangeErrors.js @@ -0,0 +1,42 @@ +const _ = require('lodash'); + +const ExchangeError = function(message) { + _.bindAll(this); + + this.name = "ExchangeError"; + this.message = message; +} +ExchangeError.prototype = new Error(); + +const ExchangeAuthenticationError = function(message) { + _.bindAll(this); + + this.name = "ExchangeAuthenticationError"; + this.message = message; +} +ExchangeAuthenticationError.prototype = new Error(); + +const RetryError = function(message) { + _.bindAll(this); + + this.name = "RetryError"; + this.retry = 5; + this.message = message; +} +RetryError.prototype = new Error(); + +const AbortError = function(message) { + _.bindAll(this); + + this.name = "AbortError"; + this.message = message; +} +AbortError.prototype = new Error(); + +module.exports = { + ExchangeError, + ExchangeAuthenticationError, + RetryError, + AbortError +}; + diff --git a/exchange/exchangeUtils.js b/exchange/exchangeUtils.js new file mode 100644 index 0000000..5243462 --- /dev/null +++ b/exchange/exchangeUtils.js @@ -0,0 +1,154 @@ +// generic low level reusuable utils for interacting with exchanges. + +const retry = require('retry'); +const errors = require('./exchangeErrors'); +const _ = require('lodash'); + +const retryInstance = (options, checkFn, callback, e) => { + if(!options) { + options = { + retries: 100, + factor: 1.2, + minTimeout: 1 * 1000, + maxTimeout: 4 * 1000 + }; + } + + let attempt = 0; + + const operation = retry.operation(options); + operation.attempt(function(currentAttempt) { + checkFn((err, result) => { + + if(!err) { + return callback(undefined, result); + } + + console.log(new Date, err.message); + + let maxAttempts = err.retry; + if(maxAttempts === true) + maxAttempts = 10; + + if(err.retry && attempt++ < maxAttempts) { + return operation.retry(err); + } + + if(err.notFatal) { + if(err.backoffDelay) { + return setTimeout(() => operation.retry(err), err.backoffDelay); + } + + return operation.retry(err); + } + + callback(err, result); + }); + }); +} + +// es6 bind all: https://github.com/posrix/es6-class-bind-all/blob/master/lib/es6ClassBindAll.js +const allMethods = targetClass => { + const propertys = Object.getOwnPropertyNames(Object.getPrototypeOf(targetClass)) + propertys.splice(propertys.indexOf('constructor'), 1) + return propertys +} + +const bindAll = (targetClass, methodNames = []) => { + for (const name of !methodNames.length ? allMethods(targetClass) : methodNames) { + targetClass[name] = targetClass[name].bind(targetClass) + } +} + +const isValidOrder = ({api, market, amount, price}) => { + let reason = false; + + // Check amount + if(amount < market.minimalOrder.amount) { + reason = 'Amount is too small'; + } + + // Some exchanges have restrictions on prices + if( + _.isFunction(api.isValidPrice) && + !api.isValidPrice(price) + ) { + reason = 'Price is not valid'; + } + + if( + _.isFunction(api.isValidLot) && + !api.isValidLot(price, amount) + ) { + reason = 'Lot size is too small'; + } + + return { + reason, + valid: !reason + } +} + + +// https://gist.github.com/jiggzson/b5f489af9ad931e3d186 +const scientificToDecimal = num => { + if(/\d+\.?\d*e[\+\-]*\d+/i.test(num)) { + const zero = '0'; + const parts = String(num).toLowerCase().split('e'); // split into coeff and exponent + const e = parts.pop(); // store the exponential part + const l = Math.abs(e); // get the number of zeros + const sign = e/l; + const coeff_array = parts[0].split('.'); + if(sign === -1) { + num = zero + '.' + new Array(l).join(zero) + coeff_array.join(''); + } else { + const dec = coeff_array[1]; + if(dec) { + l = l - dec.length; + } + num = coeff_array.join('') + new Array(l+1).join(zero); + } + } else { + // make sure we always cast to string + num = num + ''; + } + + return num; +} + +// TEMP until we have proper scheduling +const cacheFn = (fn, timeout) => { + let nextCall = false; + let cache = false; + let inflight = false; + let callbackQueue = []; + + return next => { + if(inflight) { + return callbackQueue.push(next); + } + + const now = +new Date; + if(cache && now >= nextCall) { + return next(res.error, res.result); + } + + inflight = true; + fn((error, result) => { + cache = {error, result}; + nextCall = now + timeout; + next(error, result); + callbackQueue.forEach(cb => cb(error, result)); + callbackQueue = []; + inflight = false; + }) + } +} + +module.exports = { + retry: retryInstance, + bindAll, + isValidOrder, + scientificToDecimal, + cacheFn +} \ No newline at end of file diff --git a/exchange/gekkoBroker.js b/exchange/gekkoBroker.js new file mode 100644 index 0000000..7333aec --- /dev/null +++ b/exchange/gekkoBroker.js @@ -0,0 +1,180 @@ +/* + The broker manages all communicatinn with the exchange, delegating: + + - the management of the portfolio to the portfolioManager + - the management of actual trades to "orders". +*/ + +const _ = require('lodash'); +const async = require('async'); +const events = require('events'); +const moment = require('moment'); +const checker = require('./exchangeChecker'); +const errors = require('./exchangeErrors'); +const Portfolio = require('./portfolioManager'); +// const Market = require('./market'); +const orders = require('./orders'); +const Trigger = require('./trigger'); +const exchangeUtils = require('./exchangeUtils'); +const bindAll = exchangeUtils.bindAll; +const isValidOrder = exchangeUtils.isValidOrder; + + + +class Broker { + constructor(config) { + this.config = config; + + if(config.private) { + if(this.cantTrade()) { + throw new Error(this.cantTrade()); + } + } else { + if(this.cantMonitor()) { + throw new Error(this.cantMonitor()); + } + } + + this.orders = { + // contains current open orders + open: [], + // contains all closed orders + closed: [] + } + + const slug = config.exchange.toLowerCase(); + + const API = require('./wrappers/' + slug); + + this.api = new API(config); + + this.capabilities = API.getCapabilities(); + + this.marketConfig = _.find(this.capabilities.markets, (p) => { + return _.first(p.pair) === config.currency.toUpperCase() && + _.last(p.pair) === config.asset.toUpperCase(); + }); + + if(config.customInterval) { + this.interval = config.customInterval; + this.api.interval = config.customInterval; + console.log(new Date, '[GB] setting custom interval to', config.customInterval); + } else { + this.interval = this.api.interval || 1500; + } + + this.market = config.currency.toUpperCase() + config.asset.toUpperCase(); + + if(config.private) { + this.portfolio = new Portfolio(config, this.api); + } + + bindAll(this); + } + + cantTrade() { + return checker.cantTrade(this.config); + } + + cantMonitor() { + return checker.cantMonitor(this.config); + } + + sync(callback) { + if(!this.private) { + this.setTicker(); + return; + } + + if(this.cantTrade()) { + throw new errors.ExchangeError(this.cantTrade()); + } + + this.syncPrivateData(); + } + + syncPrivateData(callback) { + async.series([ + this.setTicker, + next => setTimeout(next, this.interval), + this.portfolio.setFee.bind(this.portfolio), + next => setTimeout(next, this.interval), + this.portfolio.setBalances.bind(this.portfolio), + next => setTimeout(next, this.interval) + ], callback); + } + + setTicker(callback) { + this.api.getTicker((err, ticker) => { + + if(err) { + if(err.message) { + console.log(this.api.name, err.message); + throw err; + } else { + console.log('err not wrapped in error:', err); + throw new errors.ExchangeError(err); + } + } + + this.ticker = ticker; + + if(_.isFunction(callback)) + callback(); + }); + } + + isValidOrder(amount, price) { + return isValidOrder({ + market: this.marketConfig, + api: this.api, + amount, + price + }); + } + + createOrder(type, side, amount, parameters, handler) { + if(!this.config.private) + throw new Error('Client not authenticated'); + + if(side !== 'buy' && side !== 'sell') + throw new Error('Unknown side ' + side); + + if(!orders[type]) + throw new Error('Unknown order type'); + + const order = new orders[type]({ + api: this.api, + marketConfig: this.marketConfig, + capabilities: this.capabilities + }); + + // todo: figure out a smarter generic way + this.syncPrivateData(() => { + order.setData({ + balances: this.portfolio.balances, + ticker: this.ticker, + }); + + order.create(side, amount, parameters); + }); + + order.on('completed', summary => { + _.remove(this.orders.open, order); + this.orders.closed.push(summary); + }); + + return order; + } + + createTrigger({type, onTrigger, props}) { + return new Trigger({ + api: this.api, + type, + onTrigger, + props + }); + } +} + +module.exports = Broker; \ No newline at end of file diff --git a/exchange/orders/index.js b/exchange/orders/index.js new file mode 100644 index 0000000..4fe87d6 --- /dev/null +++ b/exchange/orders/index.js @@ -0,0 +1,5 @@ +const sticky = require('./sticky'); + +module.exports = { + sticky +} \ No newline at end of file diff --git a/exchange/orders/limit.js b/exchange/orders/limit.js new file mode 100644 index 0000000..a289f90 --- /dev/null +++ b/exchange/orders/limit.js @@ -0,0 +1,244 @@ +// NOTE: this is currently broken, see +// @link https://github.com/askmike/gekko/issues/2398 + +throw ':('; + +/* + The limit order is a simple order: + - It is created at the specified price + - If it were to cross it will throw instead (only if postOnly is specified) + - It can be moved + +*/ + +const _ = require('lodash'); +const async = require('async'); +const events = require('events'); +const moment = require('moment'); +const errors = require('../exchangeErrors'); +const BaseOrder = require('./order'); +const states = require('./states'); + +class LimitOrder extends BaseOrder { + constructor(api) { + super(api); + } + + create(side, amount, params) { + this.side = side; + + this.postOnly = params.postOnly; + + this.status = states.SUBMITTED; + this.emitStatus(); + + this.createOrder(price, amount); + } + + createOrder(price, amount) { + this.amount = this.api.roundAmount(amount); + this.price = this.api.roundPrice(price); + + // note: this assumes ticker data to be up to date + if(this.postOnly) { + if(side === 'buy' && this.price > this.data.ticker.ask) + throw new Error('Order crosses the book'); + else if(side === 'sell' && this.price < this.data.ticker.bid) + throw new Error('Order crosses the book'); + } + + this.submit({ + side: this.side, + amount: this.api.roundAmount(this.amount - alreadyFilled), + price: this.price, + alreadyFilled: this.filled + }); + } + + handleCreate(err, id) { + if(err) + throw err; + + this.status = states.OPEN; + this.emitStatus(); + + this.id = id; + + if(this.cancelling) + return this.cancel(); + + if(this.movingAmount) + return this.moveAmount(); + + if(this.movingPrice) + return this.movePrice(); + + this.timeout = setTimeout(this.checkOrder, this.checkInterval) + } + + checkOrder() { + this.checking = true; + this.api.checkOrder(this.id, this.handleCheck); + } + + handleCheck(err, result) { + if(this.cancelling || this.status === states.CANCELLED) + return; + + this.checking = false; + + if(err) + throw err; + + if(result.open) { + if(result.filledAmount !== this.filledAmount) { + this.filledAmount = result.filledAmount; + + // note: doc event API + this.emit('fill', this.filledAmount); + } + + if(this.cancelling) + return this.cancel(); + + if(this.movingAmount) + return this.moveAmount(); + + if(this.movingPrice) + return this.movePrice(); + + this.timeout = setTimeout(this.checkOrder, this.checkInterval); + return; + } + + if(!result.executed) { + // not open and not executed means it never hit the book + this.rejected(); + return; + } + + this.filled(this.price); + } + + movePrice(price) { + if(this.completed) + return; + + if(!price) + price = this.movePriceTo; + + if(this.price === this.api.roundPrice(price)) + // effectively nothing changed + return; + + if( + this.status === states.SUBMITTED || + this.status === states.MOVING || + this.checking + ) { + this.movePriceTo = price; + this.movingPrice = true; + return; + } + + this.movingPrice = false; + + this.price = this.api.roundPrice(price); + + clearTimeout(this.timeout); + + this.status = states.MOVING; + + this.api.cancelOrder(this.id, (err, filled) => { + if(err) + throw err; + + if(filled) + return this.filled(this.price); + + this.submit({ + side: this.side, + amount: this.amount, + price: this.price, + alreadyFilled: this.filled + }); + }); + } + + moveAmount(amount) { + if(this.completed) + return; + + if(!amount) + amount = this.moveAmountTo; + + if(this.amount === this.api.roundAmount(amount)) + // effectively nothing changed + return; + + if( + this.status === states.SUBMITTED || + this.status === states.MOVING || + this.checking + ) { + this.moveAmountTo = amount; + this.movingAmount = true; + return; + } + + this.movingAmount = false; + this.amount = this.api.roundAmount(amount); + + clearTimeout(this.timeout); + + this.status = states.MOVING; + this.emitStatus(); + + this.api.cancelOrder(this.id, (err, filled) => { + if(err) + throw err; + + if(filled) + return this.filled(this.price); + + this.submit({ + side: this.side, + amount: this.amount, + price: this.price, + alreadyFilled: this.filled + }); + }); + } + + cancel() { + if(this.completed) + return; + + if( + this.status === states.SUBMITTED || + this.status === states.MOVING || + this.checking + ) { + this.cancelling = true; + return; + } + + clearTimeout(this.timeout); + + this.api.cancelOrder(this.id, (err, filled) => { + if(err) + throw err; + + this.cancelling = false; + + if(filled) + return this.filled(this.price); + + this.status = states.CANCELLED; + this.emitStatus(); + this.finish(false); + }); + } +} + +module.exports = LimitOrder; \ No newline at end of file diff --git a/exchange/orders/order.js b/exchange/orders/order.js new file mode 100644 index 0000000..fd08b4d --- /dev/null +++ b/exchange/orders/order.js @@ -0,0 +1,88 @@ +const EventEmitter = require('events'); +const _ = require('lodash'); + +const exchangeUtils = require('../exchangeUtils'); +const bindAll = exchangeUtils.bindAll; +const isValidOrder = exchangeUtils.isValidOrder; +const states = require('./states'); + +// base order + +class BaseOrder extends EventEmitter { + constructor(api) { + super(); + + this.api = api; + + this.checkInterval = api.interval || 1500; + this.status = states.INITIALIZING; + + this.completed = false; + this.completing = false; + + bindAll(this); + } + + submit({side, amount, price, alreadyFilled}) { + const check = isValidOrder({ + market: this.market, + api: this.api, + amount, + price + }); + + if(!check.valid) { + if(alreadyFilled) { + // partially filled, but the remainder is too + // small. + return this.filled(); + } + + this.emit('invalidOrder', check.reason); + this.rejected(check.reason); + } + + this.api[this.side](amount, this.price, this.handleCreate); + } + + setData(data) { + this.data = data; + } + + emitStatus() { + this.emit('statusChange', this.status); + } + + cancelled() { + this.status = states.CANCELLED; + this.emitStatus(); + this.completed = true; + this.finish(); + } + + rejected(reason) { + this.rejectedReason = reason; + this.status = states.REJECTED; + this.emitStatus(); + console.log(new Date, 'sticky rejected', reason) + this.finish(); + } + + filled(price) { + this.status = states.FILLED; + this.emitStatus(); + this.completed = true; + console.log(new Date, 'sticky filled') + this.finish(true); + } + + finish(filled) { + this.completed = true; + this.emit('completed', { + status: this.status, + filled + }) + } +} + +module.exports = BaseOrder; \ No newline at end of file diff --git a/exchange/orders/states.js b/exchange/orders/states.js new file mode 100644 index 0000000..38f8118 --- /dev/null +++ b/exchange/orders/states.js @@ -0,0 +1,34 @@ +const states = { + // Not created + INITIALIZING: 'INITIALIZING', + + // Created and send to the exchange, but no acknowledgement received yet + SUBMITTED: 'SUBMITTED', + + // In the process of moving the order + MOVING: 'MOVING', + + // Order is open on the exchange + OPEN: 'OPEN', + + + CHECKING: 'CHECKING', + + CHECKED: 'CHECKED', + + // the orders below indicate a fully completed order + + + // Order is completely filled + FILLED: 'FILLED', + + // Order was succesfully cancelled + CANCELLED: 'CANCELLED', + + // Order was rejected by the exchange + REJECTED: 'REJECTED', + + ERROR: 'ERROR' +} + +module.exports = states; \ No newline at end of file diff --git a/exchange/orders/sticky.js b/exchange/orders/sticky.js new file mode 100644 index 0000000..c7165f1 --- /dev/null +++ b/exchange/orders/sticky.js @@ -0,0 +1,754 @@ +/* + The sticky order is an advanced order: + - It is created at a limit price of X + - if limit is not specified always at bbo. + - if limit is specified the price is either limit or the bbo (whichever is more favorable) + - it will readjust the order: + - if outbid is true it will outbid the current bbo (on supported exchanges) + - if outbid is false it will stick to current bbo when this moves + - If the price moves away from the order it will "stick to" the top + + TODO: + - native move +*/ + +const _ = require('lodash'); +const async = require('async'); +const events = require('events'); +const moment = require('moment'); +const errors = require('../exchangeErrors'); +const BaseOrder = require('./order'); +const states = require('./states'); + +class StickyOrder extends BaseOrder { + constructor({api, marketConfig, capabilities}) { + super(api); + + this.market = marketConfig; + this.capabilities = capabilities; + + // global async lock + this.sticking = false; + + // bound helpers + this.roundPrice = this.api.roundPrice.bind(this.api); + this.roundAmount = this.api.roundAmount.bind(this.api); + if(_.isFunction(this.api.outbidPrice)) { + this.outbidPrice = this.api.outbidPrice.bind(this.api); + } + + this.debug = this.api.name === 'Deribit2'; + this.log = m => this.debug && console.log(new Date, m); + } + + create(side, rawAmount, params = {}) { + if(this.completed || this.completing) { + return false; + } + + console.log(new Date, 'sticky create', side); + + this.side = side; + + this.amount = this.roundAmount(rawAmount); + + this.initialLimit = params.initialLimit; + + if(side === 'buy') { + if(params.limit) { + this.limit = this.roundPrice(params.limit); + } else { + this.noLimit = true; + this.limit = Infinity; + } + } else { + if(params.limit) { + this.limit = this.roundPrice(params.limit); + } else { + this.noLimit = true; + this.limit = -Infinity; + } + } + + this.status = states.SUBMITTED; + this.emitStatus(); + + this.orders = {}; + + this.outbid = params.outbid && _.isFunction(this.outbidPrice); + + if(this.data && this.data.ticker) { + this.price = this.calculatePrice(this.data.ticker); + this.createOrder(); + } else { + this.api.getTicker((err, ticker) => { + if(this.handleError(err)) { + console.log(new Date, 'error get ticker'); + return; + } + + this.price = this.calculatePrice(ticker); + this.createOrder(); + }); + } + + return this; + } + + calculatePrice(ticker) { + + const r = this.roundPrice; + + if(this.initialLimit && !this.id) { + console.log('passing initial limit of:', this.limit); + return r(this.limit); + } + + if(this.side === 'buy') { + + if(!this.noLimit && ticker.bid >= this.limit) { + return r(this.limit); + } + + if(!this.outbid) { + return r(ticker.bid); + } + + const outbidPrice = this.outbidPrice(ticker.bid, true); + + if(outbidPrice <= this.limit && outbidPrice < ticker.ask) { + return r(outbidPrice); + } else { + return r(this.limit); + } + + } else if(this.side === 'sell') { + + if(!this.noLimit && ticker.ask <= this.limit) { + return r(this.limit); + } + + if(!this.outbid) { + return r(ticker.ask); + } + + const outbidPrice = this.outbidPrice(ticker.ask, false); + + if(outbidPrice >= this.limit && outbidPrice > ticker.bid) { + return r(outbidPrice); + } else { + return r(this.limit); + } + } + } + + createOrder() { + if(this.completed || this.completing) { + return false; + } + + const alreadyFilled = this.calculateFilled(); + + this.submit({ + side: this.side, + amount: this.roundAmount(this.amount - alreadyFilled), + price: this.price, + alreadyFilled + }); + } + + // check if the last order was partially filled + // on an exchange that does not pass fill data on cancel + // see https://github.com/askmike/gekko/pull/2450 + handleInsufficientFundsError(err) { + if( + !err || + err.type !== 'insufficientFunds' || + !this.capabilities.limitedCancelConfirmation || + !this.id + ) { + return false; + } + + const id = this.id; + + setTimeout( + () => { + this.api.getOrder(id, (innerError, res) => { + if(this.handleError(innerError)) { + return; + } + + const amount = res.amount; + + if(this.orders[id].filled === amount) { + // handle original error + return this.handleError(err); + } + + this.orders[id].filled = amount; + this.emit('fill', this.calculateFilled()); + if(this.calculateFilled() >= this.amount) { + return this.filled(this.price); + } + + setTimeout(this.createOrder, this.checkInterval); + }); + }, + this.checkInterval + ); + + return true; + } + + handleCreate(err, id) { + + if(this.handleInsufficientFundsError(err)) { + return; + } + + if(this.handleError(err)) { + console.log(new Date, 'handleCreate error'); + return; + } + + if(!id) { + console.log('BLUP! no id...'); + } + + // potentailly clean up old order + if( + this.id && + this.orders[this.id] && + this.orders[this.id].filled === 0 + ) + delete this.orders[this.id]; + + // register new order + this.log(`${this.side} old id: ${this.id} new id: ${id}`); + this.id = id; + + this.orders[id] = { + price: this.price, + filled: 0 + } + + this.emit('new order', this.id); + this.emit('movelimit handled', new Date); + + this.status = states.OPEN; + this.emitStatus(); + + this.scheduleNextCheck(); + } + + initiateDefferedAction() { + // check whether we had an action pending + if(this.cancelling) { + this.cancel(); + return true; + } + + if(this.movingLimit && this.moveLimit()) { + return true; + } + + if(this.movingAmount) { + this.moveAmount(); + return true; + } + + return false; + } + + scheduleNextCheck() { + + // remove lock + this.sticking = false; + + if(this.initiateDefferedAction()) { + return; + } + + // register check + this.timeout = setTimeout(this.checkOrder, this.checkInterval); + + } + + checkOrder() { + + if(this.completed || this.completing) { + return console.log(new Date, this.side, 'checkOrder called on completed/completing order..', this.completed, this.completing); + } + + if(this.status === states.MOVING) { + return console.log(new Date, this.side, 'refusing to check, in the middle of move'); + } + + if(this.initiateDefferedAction()) { + console.log(new Date, this.side, 'skipping check logic, better things to do - 0'); + return; + } + + this.sticking = true; + const checkId = this.id; + + this.api.checkOrder(this.id, (err, result) => { + + if(!this.debug) { + if(this.ignoreCheckResult) { + this.log(this.side + ' debug ignoring check result'); + this.ignoreCheckResult = false; + return; + } + } + + if(this.status === states.MOVING) { + this.log(`${this.side} ${this.id} debug ignoring check result - in the middle of move`); + this.ignoreCheckResult = false; + return; + } + + if(checkId !== this.id) { + this.log(this.side + ' debug got check on old id ' + checkId + ', ' + this.id); + this.ignoreCheckResult = false; + return; + } + + this.status = states.CHECKED; + this.emitStatus(); + + if(this.handleError(err)) { + console.log(new Date, 'checkOrder error'); + return; + } + + if(result.open) { + if(result.filledAmount !== this.orders[this.id].filled) { + this.orders[this.id].filled = result.filledAmount; + this.emit('fill', this.calculateFilled()); + } + + // if we are already at limit we dont care where the top is + // note: might be string VS float + if(this.price == this.limit) { + this.scheduleNextCheck(); + return; + } + + if(this.initiateDefferedAction()) { + console.log(new Date, this.side, 'skipping check ticker logic, better things to do 1'); + return; + } + + this.api.getTicker((err, ticker) => { + if(this.handleError(err)) { + console.log(new Date, 'getTicker error'); + return; + } + + if(this.initiateDefferedAction()) { + console.log(new Date, this.side, 'skipping check ticker logic, better things to do 2'); + return; + } + + if(this.status === states.MOVING) { + this.log(`${this.side} ${this.id} debug ignoring check result - in the middle of move ---- 2`); + this.ignoreCheckResult = false; + return; + } + + this.ticker = ticker; + this.emit('ticker', ticker); + + const bookSide = this.side === 'buy' ? 'bid' : 'ask'; + // note: might be string VS float + if(ticker[bookSide] != this.price) { + return this.move(this.calculatePrice(ticker)); + } else { + this.scheduleNextCheck(); + } + }); + + return; + } + + // it's not open right now + // meaning we are done + this.sticking = false; + + if(!result.executed) { + // not open and not executed means it never hit the book + console.log(this.side, this.status, this.id, 'not open not executed!', result); + this.rejected(); + throw 'a'; + return; + } + + // order got filled! + this.orders[this.id].filled = this.amount; + this.emit('fill', this.amount); + this.filled(this.price); + }); + + + this.status = states.CHECKING; + this.emitStatus(); + } + + // global error handler + handleError(error) { + if(!error) { + return false; + } + + console.log(new Date, '[sticky order] FATAL ERROR', error.message); + console.log(new Date, error); + this.status = states.ERROR; + this.emitStatus(); + this.error = error; + + this.emit('error', error); + return true; + } + + // returns true if the order was fully filled + // handles partial fills on cancels calls + // on exchanges that support it. + handleCancel(filled, data) { + // it got filled before we could cancel + if(filled) { + this.orders[this.id].filled = this.amount; + this.emit('fill', this.amount); + this.filled(this.price); + return; + } + + // if we have data on partial fills + // check whether we had a partial fill + if(_.isObject(data)) { + let amountFilled = data.filled; + + if(!amountFilled && data.remaining) { + const alreadyFilled = this.calculateFilled(); + const orderAmount = this.roundAmount(this.amount - alreadyFilled); + amountFilled = this.roundAmount(orderAmount - data.remaining); + } + + if(amountFilled > this.orders[this.id].filled) { + this.orders[this.id].filled = amountFilled; + this.emit('fill', this.calculateFilled()); + } + } + + return; + } + + move(price) { + if(this.completed || this.completing) { + return false; + } + + this.status = states.MOVING; + this.emitStatus(); + + this.log(`${this.side} ${this.id} this.move 1`); + + const cancelId = this.id; + + this.api.cancelOrder(cancelId, (err, filled, data) => { + this.log(`${this.side} ${this.id} this.move 2`); + + if(this.handleError(err)) { + console.log(new Date, 'error move'); + return; + } + + // it got filled before we could cancel + if(cancelId === this.id && this.handleCancel(filled, data)) { + return; + } + + // update to new price + this.price = this.roundPrice(price); + + this.createOrder(); + }); + + return true; + } + + calculateFilled() { + let totalFilled = 0; + _.each(this.orders, (order, id) => totalFilled += order.filled); + + return totalFilled; + } + + moveLimit(limit) { + if(this.completed || this.completing) { + return false; + } + + if(!limit) { + limit = this.moveLimitTo; + // console.log(new Date, this.side, 'debug resuming move!'); + } else { + this.limitRequested = new Date; + this.log(this.side + ' received move limit'); + } + + if(this.limit === this.roundPrice(limit)) { + // effectively nothing changed + return false; + } + + if(this.cancelling) { + return false; + } + + this.emit('movelimit', new Date); + + if( + this.status === states.INITIALIZING || + this.status === states.SUBMITTED || + this.status === states.MOVING || + this.status === states.CHECKING || + this.sticking + ) { + + if( + !this.api.fillDataOnCancel || + this.status === states.MOVING || + this.status === states.SUBMITTED + ) { + // console.log(new Date, '[sticky]', this.side, 'skipping because:', this.status); + this.moveLimitTo = limit; + this.movingLimit = true; + return false; + } + } + + this.limit = this.roundPrice(limit); + this.movingLimit = false; + + const moveBuy = this.side === 'buy' && this.limit < this.price; + const moveSell = this.side === 'sell' && this.limit > this.price; + + if(moveBuy || moveSell) { + this.sticking = true; + clearTimeout(this.timeout); + + if(this.api.fillDataOnCancel && this.status === states.CHECKING) { + this.log(this.side + 'debug move overwrites running check ' + this.status + ' ' + this.id); + this.ignoreCheckResult = true; + } + + this.log(this.side + ' moving ' + this.id); + + const timeToMove = new Date - this.limitRequested; + if(timeToMove > 300) { + console.log(new Date, this.side, 'long time to move:', timeToMove); + } + this.move(this.limit); + + return true; + } + + this.emit('movelimit handled', new Date); + return false; + } + + moveAmount(amount) { + if(this.completed || this.completing) + return false; + + if(!amount) + amount = this.moveAmountTo; + + if(this.amount === this.roundAmount(amount)) + // effectively nothing changed + return true; + + if(this.calculateFilled() > this.roundAmount(amount)) { + // the amount is now below how much we have + // already filled. + this.filled(); + return false; + } + + if( + this.status === states.INITIALIZING || + this.status === states.SUBMITTED || + this.status === states.MOVING || + this.sticking + ) { + this.moveAmountTo = amount; + this.movingAmount = true; + return; + } + + this.amount = this.roundAmount(amount - this.calculateFilled()); + + if(this.amount < this.market.minimalOrder.amount) { + if(this.calculateFilled()) { + // we already filled enough of the order! + this.filled(); + return false; + } else { + this.handleError(new Error("The amount " + this.amount + " is too small.")); + } + } + + clearTimeout(this.timeout); + + this.movingAmount = false; + this.sticking = true; + + this.api.cancelOrder(this.id, (err, filled, data) => { + if(this.handleError(err)) { + console.log(new Date, 'error cancel'); + return; + } + + // it got filled before we could cancel + if(this.handleCancel(filled, data)) { + return; + } + + this.createOrder(); + }); + + return true; + } + + cancel() { + if(this.completed) + return; + + if( + this.status === states.SUBMITTED || + this.status === states.MOVING || + this.sticking + ) { + this.cancelling = true; + return; + } + + this.completing = true; + clearTimeout(this.timeout); + + this.api.cancelOrder(this.id, (err, filled, data) => { + if(this.handleError(err)) { + console.log(new Date, 'error cancel'); + return; + } + + this.cancelling = false; + + // it got filled before we could cancel + if(this.handleCancel(filled, data)) { + return; + } + + this.status = states.CANCELLED; + this.emitStatus(); + + this.finish(false); + }) + } + + createSummary(next) { + if(!this.completed) + console.log(new Date, 'createSummary BUT ORDER NOT COMPLETED!'); + + if(!next) + next = _.noop; + + const checkOrders = _.keys(this.orders) + .map(id => next => { + + if(!this.orders[id].filled) { + return next(); + } + + setTimeout(() => this.api.getOrder(id, next), this.checkInterval); + }); + + async.series(checkOrders, (err, trades) => { + // note this is a standalone function after the order is + // completed, as such we do not use the handleError flow. + if(err) { + console.log(new Date, 'error createSummary (checkOrder)') + return next(err); + } + + let price = 0; + let amount = 0; + let date = moment(0); + + _.each(trades, trade => { + if(!trade) { + return; + } + + // last fill counts + date = moment(trade.date); + price = ((price * amount) + (+trade.price * trade.amount)) / (+trade.amount + amount); + amount += +trade.amount; + }); + + const summary = { + price, + amount, + date, + side: this.side, + orders: trades.length + } + + const first = _.first(trades); + + if(first && first.fees) { + summary.fees = {}; + + _.each(trades, trade => { + if(!trade) { + return; + } + + _.each(trade.fees, (amount, currency) => { + if(!_.isNumber(summary.fees[currency])) { + summary.fees[currency] = amount; + } else { + summary.fees[currency] += amount; + } + }); + }); + } + + if(first && !_.isUndefined(first.feePercent)) { + summary.feePercent = 0; + let amount = 0; + + _.each(trades, trade => { + if(!trade || _.isUndefined(trade.feePercent)) { + return; + } + + if(trade.feePercent === 0) { + return; + } + + summary.feePercent = ((summary.feePercent * amount) + (+trade.feePercent * trade.amount)) / (+trade.amount + amount); + amount += +trade.amount; + }); + } + + this.emit('summary', summary); + next(undefined, summary); + }); + } + +} + +module.exports = StickyOrder; \ No newline at end of file diff --git a/exchange/package-lock.json b/exchange/package-lock.json new file mode 100644 index 0000000..87b2f1f --- /dev/null +++ b/exchange/package-lock.json @@ -0,0 +1,823 @@ +{ + "name": "gekko-broker", + "version": "0.6.8", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@types/caseless": { + "version": "0.12.1", + "resolved": "https://registry.npmjs.org/@types/caseless/-/caseless-0.12.1.tgz", + "integrity": "sha512-FhlMa34NHp9K5MY1Uz8yb+ZvuX0pnvn3jScRSNAb75KHGB8d3rEU6hqMs3Z2vjuytcMfRg6c5CHMc3wtYyD2/A==" + }, + "@types/form-data": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@types/form-data/-/form-data-2.2.1.tgz", + "integrity": "sha512-JAMFhOaHIciYVh8fb5/83nmuO/AHwmto+Hq7a9y8FzLDcC1KCU344XDOMEmahnrTFlHjgh4L0WJFczNIX2GxnQ==", + "requires": { + "@types/node": "*" + } + }, + "@types/node": { + "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", + "resolved": "https://registry.npmjs.org/@types/request/-/request-2.47.0.tgz", + "integrity": "sha512-/KXM5oev+nNCLIgBjkwbk8VqxmzI56woD4VUxn95O+YeQ8hJzcSmIZ1IN3WexiqBb6srzDo2bdMbsXxgXNkz5Q==", + "requires": { + "@types/caseless": "*", + "@types/form-data": "*", + "@types/node": "*", + "@types/tough-cookie": "*" + } + }, + "@types/tough-cookie": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-2.3.3.tgz", + "integrity": "sha512-MDQLxNFRLasqS4UlkWMSACMKeSm1x4Q3TxzUC7KQUsh6RK1ZrQ0VEyE3yzXcBu+K8ejVj4wuX32eUG02yNp+YQ==" + }, + "ajv": { + "version": "5.5.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz", + "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=", + "requires": { + "co": "^4.6.0", + "fast-deep-equal": "^1.0.0", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.3.0" + } + }, + "asn1": { + "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.0" + } + }, + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" + }, + "async": { + "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" + } + }, + "async-limiter": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.0.tgz", + "integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg==" + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" + }, + "aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=" + }, + "aws4": { + "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.4.0", + "regenerator-runtime": "^0.11.0" + } + }, + "bcrypt-pbkdf": { + "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.3" + } + }, + "bignumber.js": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-6.0.0.tgz", + "integrity": "sha512-x247jIuy60/+FtMRvscqfxtVHQf8AGx2hm9c6btkgC0x/hp9yt+teISNhvF8WlwRkCc5yF2fDECH8SIMe8j+GA==" + }, + "binance": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/binance/-/binance-1.3.3.tgz", + "integrity": "sha512-1eV2QUoH/Z0FZPiGjigJg4udXV9Uu6Clr0Sg1xsX3xStgPfzXz0juA3mllQIiIaHx7dmfAQgEiZIyeJLx5ajag==", + "requires": { + "request": "^2.81.0", + "underscore": "^1.8.3", + "ws": "^3.1.0" + } + }, + "bintrees": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bintrees/-/bintrees-1.0.2.tgz", + "integrity": "sha1-SfiW1uhYpKSZ34XDj7OZua/4QPg=" + }, + "bitfinex-api-node": { + "version": "2.0.0-beta.1", + "resolved": "https://registry.npmjs.org/bitfinex-api-node/-/bitfinex-api-node-2.0.0-beta.1.tgz", + "integrity": "sha512-UVKh4PAaijplm/VWs3ZLONQo8mhF0bemDZjEQCKtJteNlB+crmqRp/fPuWC2ZTYX2ZQ//J0zriOhEUiuwwGSDA==", + "requires": { + "bignumber.js": "^6.0.0", + "cbq": "0.0.1", + "crc-32": "^1.2.0", + "debug": "^2.2.0", + "lodash": "^4.17.4", + "lodash.throttle": "^4.1.1", + "request": "^2.67.0", + "request-promise": "^4.2.0", + "ws": "^3.0.0" + } + }, + "bitx": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/bitx/-/bitx-1.5.1.tgz", + "integrity": "sha1-+h9cYuhIEFse1Pj2tIIwd4jmMq0=" + }, + "bluebird": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.1.tgz", + "integrity": "sha512-MKiLiV+I1AA596t9w1sQJ8jkiSr5+ZKi0WKrYGUn6d1Fx+Ij4tIj+m2WMQSGczs5jZVxV339chE8iwk6F64wjA==" + }, + "caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" + }, + "cbq": { + "version": "0.0.1", + "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.49.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.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" + } + }, + "combined-stream": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.6.tgz", + "integrity": "sha1-cj599ugBrFYTETp+RFqbactjKBg=", + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "core-js": { + "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", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" + }, + "crc-32": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.0.tgz", + "integrity": "sha512-1uBwHxF+Y/4yF5G48fwnKq6QsIXheor3ZLPT80yGBV1oEUwpPojlEhQbWKVw1VwcTQyMGHK1/XMmTjmlsmTTGA==", + "requires": { + "exit-on-epipe": "~1.0.1", + "printj": "~1.1.0" + } + }, + "crypto-js": { + "version": "3.1.9-1", + "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-3.1.9-1.tgz", + "integrity": "sha1-/aGedh/Ad+Af+/3G6f38WeiAbNg=" + }, + "dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "requires": { + "assert-plus": "^1.0.0" + } + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "decompress-response": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", + "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=", + "requires": { + "mimic-response": "^1.0.0" + } + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" + }, + "duplexer3": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", + "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=" + }, + "ecc-jsbn": { + "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.0", + "safer-buffer": "^2.1.0" + } + }, + "exit-on-epipe": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/exit-on-epipe/-/exit-on-epipe-1.0.1.tgz", + "integrity": "sha512-h2z5mrROTxce56S+pnvAV890uu7ls7f1kEvVGJbw1OlFH3/mlJ5bkXu0KRyW94v37zzHPiUd55iLn3DA7TjWpw==" + }, + "extend": { + "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", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" + }, + "fast-deep-equal": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", + "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=" + }, + "fast-json-stable-stringify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", + "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=" + }, + "forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" + }, + "form-data": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.2.tgz", + "integrity": "sha1-SXBJi+YEwgwAXU9cI67NIda0kJk=", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "1.0.6", + "mime-types": "^2.1.12" + } + }, + "gdax": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/gdax/-/gdax-0.7.0.tgz", + "integrity": "sha512-lJHXlGchJVNtql8VWH+Idalehl5T5N6O9g5MUEW7IOuPtsbb7D15vgz6MOx1NgSyZe0fSIINv9s0HxujYB3sqg==", + "requires": { + "@types/request": "2.47.0", + "bignumber.js": "^5.0.0", + "bintrees": "^1.0.1", + "request": "^2.81.0", + "ws": "^4.0.0" + }, + "dependencies": { + "bignumber.js": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-5.0.0.tgz", + "integrity": "sha512-KWTu6ZMVk9sxlDJQh2YH1UOnfDP8O8TpxUxgQG/vKASoSnEjK9aVuOueFaPcQEYQ5fyNXNTOYwYw3099RYebWg==" + }, + "ws": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-4.1.0.tgz", + "integrity": "sha512-ZGh/8kF9rrRNffkLFV4AzhvooEclrOH0xaugmqGsIfFgOE/pIz4fMc4Ef+5HSQqTEug2S9JZIWDR47duDSLfaA==", + "requires": { + "async-limiter": "~1.0.0", + "safe-buffer": "~5.1.0" + } + } + } + }, + "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" + } + }, + "gekko-broker-poloniex": { + "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" + } + }, + "get-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", + "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=" + }, + "getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "requires": { + "assert-plus": "^1.0.0" + } + }, + "got": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/got/-/got-7.1.0.tgz", + "integrity": "sha512-Y5WMo7xKKq1muPsxD+KmrR8DH5auG7fBdDVueZwETwV6VytKyU9OX/ddpq2/1hp1vIPvVb4T81dKQz3BivkNLw==", + "requires": { + "decompress-response": "^3.2.0", + "duplexer3": "^0.1.4", + "get-stream": "^3.0.0", + "is-plain-obj": "^1.1.0", + "is-retry-allowed": "^1.0.0", + "is-stream": "^1.0.0", + "isurl": "^1.0.0-alpha5", + "lowercase-keys": "^1.0.0", + "p-cancelable": "^0.3.0", + "p-timeout": "^1.1.1", + "safe-buffer": "^5.0.1", + "timed-out": "^4.0.0", + "url-parse-lax": "^1.0.0", + "url-to-options": "^1.0.1" + } + }, + "har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=" + }, + "har-validator": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.0.3.tgz", + "integrity": "sha1-ukAsJmGU8VlW7xXg/PJCmT9qff0=", + "requires": { + "ajv": "^5.1.0", + "har-schema": "^2.0.0" + } + }, + "has-symbol-support-x": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/has-symbol-support-x/-/has-symbol-support-x-1.4.2.tgz", + "integrity": "sha512-3ToOva++HaW+eCpgqZrCfN51IPB+7bJNVT6CUATzueB5Heb8o6Nam0V3HG5dlDvZU1Gn5QLcbahiKw/XVk5JJw==" + }, + "has-to-string-tag-x": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/has-to-string-tag-x/-/has-to-string-tag-x-1.4.1.tgz", + "integrity": "sha512-vdbKfmw+3LoOYVr+mtxHaX5a96+0f3DljYd8JOqvOLsf5mw2Otda2qCDT9qRqLAhrjyQ0h7ual5nOiASpsGNFw==", + "requires": { + "has-symbol-support-x": "^1.4.1" + } + }, + "http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", + "requires": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + } + }, + "is-object": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-object/-/is-object-1.0.1.tgz", + "integrity": "sha1-iVJojF7C/9awPsyF52ngKQMINHA=" + }, + "is-plain-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", + "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=" + }, + "is-retry-allowed": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-1.1.0.tgz", + "integrity": "sha1-EaBgVotnM5REAz0BJaYaINVk+zQ=" + }, + "is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" + }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" + }, + "isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" + }, + "isurl": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isurl/-/isurl-1.0.0.tgz", + "integrity": "sha512-1P/yWsxPlDtn7QeRD+ULKQPaIaN6yF368GZ2vDfv0AL0NwpStafjWCDDdn0k8wgFMWpVAqG7oJhxHnlud42i9w==", + "requires": { + "has-to-string-tag-x": "^1.2.0", + "is-object": "^1.0.1" + } + }, + "jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", + "optional": true + }, + "json-schema": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=" + }, + "json-schema-traverse": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", + "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=" + }, + "json-stringify-safe": { + "version": "5.0.1", + "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", + "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", + "requires": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.2.3", + "verror": "1.10.0" + } + }, + "kraken-api": { + "version": "github:askmike/npm-kraken-api#a69dfb3eb296b9c795cfa48dd31edcdc1b3d5398", + "from": "github:askmike/npm-kraken-api#a69dfb3eb296b9c795cfa48dd31edcdc1b3d5398", + "requires": { + "got": "^7.1.0", + "qs": ">=6.4.0" + } + }, + "lodash": { + "version": "4.17.10", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.10.tgz", + "integrity": "sha512-UejweD1pDoXu+AD825lWwp4ZGtSwgnpZxb3JDViD7StjQz+Nb/6l093lx4OQ0foGWNRoc19mWy7BzL+UAK2iVg==" + }, + "lodash.throttle": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz", + "integrity": "sha1-wj6RtxAkKscMN/HhzaknTMOb8vQ=" + }, + "lowercase-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", + "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==" + }, + "mime-db": { + "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.19", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.19.tgz", + "integrity": "sha512-P1tKYHVSZ6uFo26mtnve4HQFE3koh1UWVkp8YUC+ESBHe945xWSoXuHHiGarDqcEZ+whpCDnlNw5LON0kLo+sw==", + "requires": { + "mime-db": "~1.35.0" + } + }, + "mimic-response": { + "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.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", + "integrity": "sha1-7nMCrejBvvR28wG4yR9cxRpIdhI=" + }, + "oauth-sign": { + "version": "0.8.2", + "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", + "integrity": "sha512-RVbZPLso8+jFeq1MfNvgXtCRED2raz/dKpacfTNxsx6pLEpEomM7gah6VeHSYV3+vo0OAi4MkArtQcWWXuQoyw==" + }, + "p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=" + }, + "p-timeout": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-1.2.1.tgz", + "integrity": "sha1-XrOzU7f86Z8QGhA4iAuwVOu+o4Y=", + "requires": { + "p-finally": "^1.0.0" + } + }, + "performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" + }, + "prepend-http": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz", + "integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=" + }, + "printj": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/printj/-/printj-1.1.2.tgz", + "integrity": "sha512-zA2SmoLaxZyArQTOPj5LXecR+RagfPSU5Kw1qP+jkWeNlrq+eJZyY2oS68SU1Z/7/myXM4lo9716laOFAVStCQ==" + }, + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" + }, + "qs": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" + }, + "regenerator-runtime": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", + "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==" + }, + "request": { + "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.6.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.5", + "extend": "~3.0.1", + "forever-agent": "~0.6.1", + "form-data": "~2.3.1", + "har-validator": "~5.0.3", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.17", + "oauth-sign": "~0.8.2", + "performance-now": "^2.1.0", + "qs": "~6.5.1", + "safe-buffer": "^5.1.1", + "tough-cookie": "~2.3.3", + "tunnel-agent": "^0.6.0", + "uuid": "^3.1.0" + } + }, + "request-promise": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/request-promise/-/request-promise-4.2.2.tgz", + "integrity": "sha1-0epG1lSm7k+O5qT+oQGMIpEZBLQ=", + "requires": { + "bluebird": "^3.5.0", + "request-promise-core": "1.1.1", + "stealthy-require": "^1.1.0", + "tough-cookie": ">=2.3.3" + } + }, + "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.13.1" + } + }, + "retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", + "integrity": "sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=" + }, + "safe-buffer": { + "version": "5.1.2", + "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.17" + } + }, + "sshpk": { + "version": "1.14.2", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.14.2.tgz", + "integrity": "sha1-xvxhZIo9nE52T9P8306hBeSSupg=", + "requires": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + } + }, + "stealthy-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz", + "integrity": "sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks=" + }, + "therocktrading": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/therocktrading/-/therocktrading-0.9.0.tgz", + "integrity": "sha512-yHYefoQ05GJsytI6QsAbm3G+cnQta9Q0wC+4etmxDiesWvtSeH87PN3JIpq56UYsUxjn37mgiK2c5oSTK4yRpQ==", + "requires": { + "underscore": "1.4.4" + }, + "dependencies": { + "underscore": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.4.4.tgz", + "integrity": "sha1-YaajIBBiKvoHljvzJSA88SI51gQ=" + } + } + }, + "timed-out": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/timed-out/-/timed-out-4.0.1.tgz", + "integrity": "sha1-8y6srFoXW+ol1/q1Zas+2HQe9W8=" + }, + "tough-cookie": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.4.tgz", + "integrity": "sha512-TZ6TTfI5NtZnuyy/Kecv+CnoROnyXn2DN97LontgQpCwsX2XyLYCC0ENhYkehSOwAp8rTQKc/NUIF7BkQ5rKLA==", + "requires": { + "punycode": "^1.4.1" + } + }, + "tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "requires": { + "safe-buffer": "^5.0.1" + } + }, + "tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "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.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", + "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-1.0.0.tgz", + "integrity": "sha1-evjzA2Rem9eaJy56FKxovAYJ2nM=", + "requires": { + "prepend-http": "^1.0.1" + } + }, + "url-to-options": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/url-to-options/-/url-to-options-1.0.1.tgz", + "integrity": "sha1-FQWgOiiaSMvXpDTvuu7FBV9WM6k=" + }, + "uuid": { + "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", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "requires": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.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.2.0", + "nan": "^2.3.3", + "typedarray-to-buffer": "^3.1.2", + "yaeti": "^0.0.6" + } + }, + "ws": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-3.3.3.tgz", + "integrity": "sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA==", + "requires": { + "async-limiter": "~1.0.0", + "safe-buffer": "~5.1.0", + "ultron": "~1.1.0" + } + }, + "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 new file mode 100644 index 0000000..9e115bb --- /dev/null +++ b/exchange/package.json @@ -0,0 +1,40 @@ +{ + "name": "gekko-broker", + "version": "0.6.8", + "description": "Gekko's order execution library for bitcoin & crypto exchanges", + "main": "gekkoBroker.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "repository": { + "type": "git", + "url": "github.com/askmike/gekko" + }, + "keywords": [ + "crypto", + "bitcoin", + "exchange", + "execution", + "trade" + ], + "author": "Mike van Rossum ", + "license": "MIT", + "dependencies": { + "async": "^2.6.0", + "binance": "^1.3.3", + "bitfinex-api-node": "^2.0.0-beta", + "bitx": "^1.5.0", + "bluebird": "^3.5.1", + "coinfalcon": "^1.0.3", + "crypto-js": "^3.1.9-1", + "gdax": "^0.7.0", + "gekko-bittrex": "^0.8.5", + "gekko-broker-poloniex": "^0.0.12", + "kraken-api": "askmike/npm-kraken-api#a69dfb3eb296b9c795cfa48dd31edcdc1b3d5398", + "lodash": "^4.17.5", + "moment": "^2.22.1", + "request-promise": "^4.2.2", + "retry": "^0.12.0", + "therocktrading": "^0.9.0" + } +} diff --git a/exchange/portfolioManager.js b/exchange/portfolioManager.js new file mode 100644 index 0000000..c9b00d0 --- /dev/null +++ b/exchange/portfolioManager.js @@ -0,0 +1,87 @@ +/* + The Portfolio class holds data about the portfolio +*/ + +const _ = require('lodash'); +const async = require('async'); +const errors = require('./exchangeErrors'); +// const EventEmitter = require('events'); + +class Portfolio { + constructor(config, api) { + _.bindAll(this); + this.config = config; + this.api = api; + this.balances = {}; + this.fee = null; + } + + getBalance(fund) { + return this.getFund(fund).amount; + } + + // return the [fund] based on the data we have in memory + getFund(fund) { + return _.find(this.balances, function(f) { return f.name === fund}); + } + + // convert into the portfolio expected by the performanceAnalyzer + convertBalances(asset,currency) { // rename? + var asset = _.find(this.balances, a => a.name === this.config.asset).amount; + var currency = _.find(this.balances, a => a.name === this.config.currency).amount; + + return { + currency, + asset, + balance: currency + (asset * this.ticker.bid) + } + } + + setBalances(callback) { + let set = (err, fullPortfolio) => { + if(err) { + console.log(err); + throw new errors.ExchangeError(err); + } + + // only include the currency/asset of this market + const balances = [ this.config.currency, this.config.asset ] + .map(name => { + let item = _.find(fullPortfolio, {name}); + + if(!item) { + // assume we have 0 + item = { name, amount: 0 }; + } + + return item; + }); + + this.balances = balances; + + if(_.isFunction(callback)) + callback(); + } + + this.api.getPortfolio(set); + } + + setFee(callback) { + this.api.getFee((err, fee) => { + if(err) + throw new errors.ExchangeError(err); + + this.fee = fee; + + if(_.isFunction(callback)) + callback(); + }); + } + + setTicker(ticker) { + this.ticker = ticker; + } + +} + +module.exports = Portfolio \ No newline at end of file diff --git a/exchange/trigger.js b/exchange/trigger.js new file mode 100644 index 0000000..2e72a76 --- /dev/null +++ b/exchange/trigger.js @@ -0,0 +1,80 @@ +// wraps around a low level trigger and feeds +// it live market data. + +const _ = require('lodash'); + +const exchangeUtils = require('./exchangeUtils'); +const bindAll = exchangeUtils.bindAll; + +const triggers = require('./triggers'); + +// @param api: a gekko broker wrapper instance +// @param type: type of trigger to wrap +// @param props: properties to feed to trigger +class Trigger { + constructor({api, type, props, onTrigger}) { + this.onTrigger = onTrigger; + this.api = api; + + this.isLive = true; + + // note: we stay on the safe side and trigger + // as soon as the bid goes below trail. + this.tickerProp = 'bid'; + + if(!_.has(triggers, type)) { + throw new Error('Gekko Broker does not know trigger ' + type); + } + + this.CHECK_INTERVAL = this.api.interval * 10; + + bindAll(this); + this.trigger = new triggers[type]({ + onTrigger: this.propogateTrigger, + ...props + }) + + this.scheduleFetch(); + } + + scheduleFetch() { + this.timout = setTimeout(this.fetch, this.CHECK_INTERVAL); + } + + fetch() { + if(!this.isLive) { + return; + } + this.api.getTicker(this.processTicker) + } + + processTicker(err, ticker) { + if(!this.isLive) { + return; + } + + if(err) { + return console.log('[GB/trigger] failed to fetch ticker:', err); + } + + this.price = ticker[this.tickerProp]; + + this.trigger.updatePrice(this.price); + this.scheduleFetch(); + } + + cancel() { + this.isLive = false; + clearTimeout(this.timout); + } + + propogateTrigger(payload) { + if(!this.isLive) { + return; + } + this.isLive = false; + this.onTrigger(payload); + } +} + +module.exports = Trigger; \ No newline at end of file diff --git a/exchange/triggers/index.js b/exchange/triggers/index.js new file mode 100644 index 0000000..9824182 --- /dev/null +++ b/exchange/triggers/index.js @@ -0,0 +1,3 @@ +const trailingStop = require('./trailingStop'); + +module.exports = { trailingStop }; \ No newline at end of file diff --git a/exchange/triggers/trailingStop.js b/exchange/triggers/trailingStop.js new file mode 100644 index 0000000..89057d9 --- /dev/null +++ b/exchange/triggers/trailingStop.js @@ -0,0 +1,62 @@ +const EventEmitter = require('events'); + +// Note: as of now only supports trailing the price going up (after +// a buy), on trigger (when the price moves down) you should sell. + + +// @param initialPrice: initial price, preferably buy price +// @param trail: fixed offset from the price +// @param onTrigger: fn to call when the stop triggers +class TrailingStop extends EventEmitter { + constructor({trail, initialPrice, onTrigger}) { + super(); + + this.trail = trail; + this.isLive = true; + this.onTrigger = onTrigger; + + this.previousPrice = initialPrice; + this.trailingPoint = initialPrice - this.trail; + } + + updatePrice(price) { + if(!this.isLive) { + return; + } + + if(price > this.trailingPoint + this.trail) { + this.trailingPoint = price - this.trail; + } + + this.previousPrice = price; + + if(price <= this.trailingPoint) { + this.trigger(); + } + } + + updateTrail(trail) { + if(!this.isLive) { + return; + } + + this.trail = trail; + this.trailingPoint = this.previousPrice - this.trail; + // recheck whether moving the trail triggered. + this.updatePrice(this.previousPrice); + } + + trigger() { + if(!this.isLive) { + return; + } + + this.isLive = false; + if(this.onTrigger) { + this.onTrigger(this.previousPrice); + } + this.emit('trigger', this.previousPrice); + } +} + +module.exports = TrailingStop; \ No newline at end of file diff --git a/exchange/util/genMarketFiles/update-binance.js b/exchange/util/genMarketFiles/update-binance.js new file mode 100644 index 0000000..f0acd6a --- /dev/null +++ b/exchange/util/genMarketFiles/update-binance.js @@ -0,0 +1,51 @@ +const _ = require('lodash'); +const fs = require('fs'); +const request = require('request-promise'); +const Promise = require('bluebird'); + + +let getOrderMinSize = currency => { + if (currency === 'BTC') return 0.001; + else if (currency === 'ETH') return 0.01; + else if (currency === 'USDT') return 10; + else return 1; +}; + +const options = { + url: 'https://www.binance.com/exchange/public/product', + headers: { + Connection: 'keep-alive', + 'User-Agent': 'Request-Promise', + }, + json: true, +}; + +request(options) + .then(body => { + if (!body && !body.data) { + throw new Error('Unable to fetch product list, response was empty'); + } + + let assets = _.uniqBy(_.map(body.data, market => market.baseAsset)); + let currencies = _.uniqBy(_.map(body.data, market => market.quoteAsset)); + let pairs = _.map(body.data, market => { + return { + pair: [market.quoteAsset, market.baseAsset], + minimalOrder: { + amount: parseFloat(market.minTrade), + price: parseFloat(market.tickSize), + order: getOrderMinSize(market.quoteAsset), + }, + }; + }); + + return { assets: assets, currencies: currencies, markets: pairs }; + }) + .then(markets => { + fs.writeFileSync('../../wrappers/binance-markets.json', JSON.stringify(markets, null, 2)); + console.log(`Done writing Binance market data`); + }) + .catch(err => { + console.log(`Couldn't import products from Binance`); + console.log(err); + }); diff --git a/exchange/util/genMarketFiles/update-bitfinex.js b/exchange/util/genMarketFiles/update-bitfinex.js new file mode 100644 index 0000000..9ec9950 --- /dev/null +++ b/exchange/util/genMarketFiles/update-bitfinex.js @@ -0,0 +1,54 @@ +const _ = require('lodash'); +const fs = require('fs'); +const request = require('request-promise'); +const Promise = require('bluebird'); + +request({ + url: 'https://api.bitfinex.com/v1/symbols_details', + headers: { + Connection: 'keep-alive', + 'User-Agent': 'Request-Promise', + }, + json: true, +}) +.then(body => { + if (!body) { + throw new Error('Unable to fetch list of assets, response was empty'); + } + + return body; +}) +.then(results => { + let assets = _.uniq(_.map(results, market => { + return market.pair.substring(0, 3).toUpperCase(); + })); + + let currencies = _.uniq(_.map(results, market => { + return market.pair.substring(3, 6).toUpperCase(); + })); + + let markets = _.map(results, market => { + return { + pair: [ + market.pair.substring(3, 6).toUpperCase(), + market.pair.substring(0, 3).toUpperCase() + ], + minimalOrder: { + amount: market.minimum_order_size, + unit: 'asset', + }, + }; + }); + + return { assets: assets, currencies: currencies, markets: markets }; +}) +.then(markets => { + fs.writeFileSync('../../wrappers/bitfinex-markets.json', JSON.stringify(markets, null, 2)); + console.log(`Done writing Bitfinex market data`); +}) +.catch(err => { + console.log(`Couldn't import products from Bitfinex`); + console.log(err); +}); + + diff --git a/exchange/util/genMarketFiles/update-coinbase.js b/exchange/util/genMarketFiles/update-coinbase.js new file mode 100644 index 0000000..90b5252 --- /dev/null +++ b/exchange/util/genMarketFiles/update-coinbase.js @@ -0,0 +1,54 @@ +const _ = require('lodash'); +const fs = require('fs'); +const request = require('request-promise'); +const Promise = require('bluebird'); + +request({ + url: 'https://api.pro.coinbase.com/products', + headers: { + Connection: 'keep-alive', + 'User-Agent': 'Request-Promise', + }, + json: true, +}) +.then(body => { + if (!body) { + throw new Error('Unable to fetch list of assets, response was empty'); + } + + return body; +}) +.then(results => { + let assets = _.uniq(_.map(results, market => { + return market.base_currency.toUpperCase(); + })); + + let currencies = _.uniq(_.map(results, market => { + return market.quote_currency.toUpperCase(); + })); + + let markets = _.map(results, market => { + return { + pair: [ + market.quote_currency.toUpperCase(), + market.base_currency.toUpperCase() + ], + minimalOrder: { + amount: market.base_min_size, + unit: 'asset', + }, + }; + }); + + return { assets: assets, currencies: currencies, markets: markets }; +}) +.then(markets => { + fs.writeFileSync('../../wrappers/coinbase-markets.json', JSON.stringify(markets, null, 2)); + console.log(`Done writing Coinbase market data`); +}) +.catch(err => { + console.log(`Couldn't import products from Coinbase`); + console.log(err); +}); + + diff --git a/exchange/util/genMarketFiles/update-coinfalcon.js b/exchange/util/genMarketFiles/update-coinfalcon.js new file mode 100644 index 0000000..dd58647 --- /dev/null +++ b/exchange/util/genMarketFiles/update-coinfalcon.js @@ -0,0 +1,45 @@ +const _ = require('lodash'); +const fs = require('fs'); +const request = require('request-promise'); +const Promise = require('bluebird'); + +const options = { + url: 'https://coinfalcon.com/api/v1/markets', + headers: { + Connection: 'keep-alive', + 'User-Agent': 'Request-Promise', + }, + json: true, +}; + +request(options) + .then(body => { + if (!body && !body.data) { + throw new Error('Unable to fetch product list, response was empty'); + } + + let assets = _.uniq(_.map(body.data, market => market.name.split('-')[0])); + let currencies = _.uniq(_.map(body.data, market => market.name.split('-')[1])); + let pairs = _.map(body.data, market => { + var currency = market.name.split('-')[1]; + var asset = market.name.split('-')[0]; + return { + pair: [currency, asset], + minimalOrder: { + amount: parseFloat(market.min_volume), + price: parseFloat(market.min_price), + order: 0.0 + }, + }; + }); + + return { assets: assets, currencies: currencies, markets: pairs }; + }) + .then(markets => { + fs.writeFileSync('../../wrappers/coinfalcon-markets.json', JSON.stringify(markets, null, 2)); + console.log(`Done writing CoinFalcon market data`); + }) + .catch(err => { + console.log(`Couldn't import products from CoinFalcon`); + console.log(err); + }); diff --git a/exchange/util/genMarketFiles/update-kraken.js b/exchange/util/genMarketFiles/update-kraken.js new file mode 100644 index 0000000..b6110d1 --- /dev/null +++ b/exchange/util/genMarketFiles/update-kraken.js @@ -0,0 +1,152 @@ +const _ = require('lodash'); +const fs = require('fs'); +const request = require('request-promise'); +const Promise = require('bluebird'); + +// Minimum amounts are not queryable, get them here +// https://support.kraken.com/hc/en-us/articles/205893708-What-is-the-minimum-order-size- + +let getMinTradeSize = asset => { + let minTradeSize = 0.01; + switch (asset) { + case 'XREP': + minTradeSize = '0.3' + break; + case 'XXBT': + minTradeSize = '0.002' + break; + case 'BCH': + minTradeSize = '0.002' + break; + case 'ADA': + minTradeSize = '1' + break; + case 'DASH': + minTradeSize = '0.03' + break; + case 'XXDG': + minTradeSize = '3000' + break; + case 'EOS': + minTradeSize = '3.0' + break; + case 'XETH': + minTradeSize = '0.02' + break; + case 'XETC': + minTradeSize = '0.3' + break; + case 'GNO': + minTradeSize = '0.03' + break; + case 'XICN': + minTradeSize = '2' + break; + case 'XLTC': + minTradeSize = '0.1' + break; + case 'XMLN': + minTradeSize = '0.1' + break; + case 'XXMR': + minTradeSize = '0.1' + break; + case 'QTUM': + minTradeSize = '0.1' + break; + case 'XXRP': + minTradeSize = '30' + break; + case 'XXLM': + minTradeSize = '30' + break; + case 'USDT': + minTradeSize = '5' + break; + case 'XTZ': + minTradeSize = '1' + break; + case 'XZEC': + minTradeSize = '0.03' + break; + default: + break; + } + + return minTradeSize; +} + +let assetPromise = request({ + url: 'https://api.kraken.com/0/public/Assets', + headers: { + Connection: 'keep-alive', + 'User-Agent': 'Request-Promise', + }, + json: true, +}).then(body => { + if (!body || !body.result) { + throw new Error('Unable to fetch list of assets, response was empty') + } else if (!_.isEmpty(body.error)) { + throw new Error(`Unable to fetch list of assets: ${body.error}`); + } + + return body.result; +}); + +let assetPairsPromise = request({ + url: 'https://api.kraken.com/0/public/AssetPairs', + headers: { + Connection: 'keep-alive', + 'User-Agent': 'Request-Promise', + }, + json: true, +}).then(body => { + if (!body || !body.result) { + throw new Error('Unable to fetch list of assets, response was empty') + } else if (!_.isEmpty(body.error)) { + throw new Error(`Unable to fetch list of assets: ${body.error}`); + } + + return body.result; +}); + +Promise.all([assetPromise, assetPairsPromise]) + .then(results => { + let assets = _.uniq(_.map(results[1], market => { + return results[0][market.base].altname; + })); + + let currencies = _.uniq(_.map(results[1], market => { + return results[0][market.quote].altname; + })); + + let marketKeys = _.filter(_.keys(results[1]), k => { return !k.endsWith('.d'); }); + let markets = _.map(marketKeys, k => { + let market = results[1][k]; + let asset = results[0][market.base]; + let currency = results[0][market.quote]; + return { + pair: [currency.altname, asset.altname], + prefixed: [market.quote, market.base], + book: k, + minimalOrder: { + amount: getMinTradeSize(market.base), + unit: 'asset', + }, + pricePrecision: market.pair_decimals, + amountPrecision: market.lot_decimals + }; + }); + + return { assets: assets, currencies: currencies, markets: markets }; + }) + .then(markets => { + fs.writeFileSync('../../wrappers/kraken-markets.json', JSON.stringify(markets, null, 2)); + console.log(`Done writing Kraken market data`); + }) + .catch(err => { + console.log(`Couldn't import products from Kraken`); + console.log(err); + }); + + diff --git a/exchange/wrappers/DEBUG_exchange-simulator.js b/exchange/wrappers/DEBUG_exchange-simulator.js new file mode 100644 index 0000000..5df3e78 --- /dev/null +++ b/exchange/wrappers/DEBUG_exchange-simulator.js @@ -0,0 +1,72 @@ +// Fake exchanges: used to test purposes to develop Gekko (works without internet). + +const _ = require('lodash'); +const moment = require('moment'); + +const TREND_DURATION = 1000; + +const Trader = function() { + this.name = 'Exchange Simulator'; + this.at = moment().subtract(30, 'minutes'); + + + // fake data + this.price = 100; + this.trend = 'up'; + this.tid = 0; +} + +Trader.prototype.getTrades = function(since, cb) { + const amount = moment().diff(this.at, 'seconds'); + + const trades = _.range(amount).map(() => { + + this.tid++; + + if(this.tid % TREND_DURATION === 0) { + if(this.trend === 'up') + this.trend = 'down'; + else + this.trend = 'up'; + } + + if(this.trend === 'up') + this.price += Math.random(); + else + this.price -= Math.random(); + + return { + date: this.at.add(1, 'seconds').unix(), + price: this.price, + amount: Math.random() * 100, + tid: this.tid + } + }); + + console.log( + `[EXCHANGE SIMULATOR] emitted ${amount} fake trades, up until ${this.at.format('YYYY-MM-DD HH:mm:ss')}.` + ); + + cb(null, trades); +} + +Trader.getCapabilities = function () { + return { + name: 'Exchange Simulator', + slug: 'DEBUG_exchange-simulator', + currencies: ['USD'], + assets: ['BTC', 'BTC'], + maxTradesAge: 60, + maxHistoryFetch: null, + markets: [ + { pair: ['USD', 'BTC'], minimalOrder: { amount: 5, unit: 'currency' } }, + ], + requires: ['key', 'secret', 'username'], + fetchTimespan: 60, + tid: 'tid', + tradable: false + }; +} + +module.exports = Trader; + diff --git a/exchange/wrappers/binance-markets.json b/exchange/wrappers/binance-markets.json new file mode 100644 index 0000000..f08eceb --- /dev/null +++ b/exchange/wrappers/binance-markets.json @@ -0,0 +1,4599 @@ +{ + "assets": [ + "NULS", + "BNB", + "NEO", + "LINK", + "SALT", + "IOTA", + "ETC", + "AST", + "KNC", + "WTC", + "SNGLS", + "EOS", + "SNT", + "MCO", + "BTC", + "OAX", + "OMG", + "GAS", + "BQX", + "QTUM", + "BNT", + "DNT", + "SNM", + "STRAT", + "ZRX", + "FUN", + "LTC", + "ETH", + "XVG", + "ADA", + "ADX", + "AE", + "AGI", + "AION", + "AMB", + "APPC", + "ARDR", + "ARK", + "ARN", + "BAT", + "BCD", + "BCHABC", + "BCHSV", + "BCPT", + "BLZ", + "BRD", + "BTG", + "BTS", + "CDT", + "CLOAK", + "CMT", + "CND", + "CVC", + "DASH", + "DATA", + "DCR", + "DENT", + "DGD", + "DLT", + "DOCK", + "EDO", + "ELF", + "ENG", + "ENJ", + "EVX", + "FUEL", + "GNT", + "GO", + "GRS", + "GTO", + "GVT", + "GXS", + "HC", + "HOT", + "ICX", + "INS", + "IOST", + "IOTX", + "KEY", + "KMD", + "LEND", + "LOOM", + "LRC", + "LSK", + "LUN", + "MANA", + "MDA", + "MFT", + "MITH", + "MOD", + "MTH", + "MTL", + "NANO", + "NAS", + "NAV", + "NCASH", + "NEBL", + "NPXS", + "NXS", + "ONT", + "OST", + "PAX", + "PHX", + "PIVX", + "POA", + "POE", + "POLY", + "POWR", + "PPT", + "QKC", + "QLC", + "QSP", + "RCN", + "RDN", + "REN", + "REP", + "REQ", + "RLC", + "RVN", + "SC", + "SKY", + "STEEM", + "STORJ", + "STORM", + "SUB", + "SYS", + "THETA", + "TNB", + "TNT", + "TRX", + "TUSD", + "USDC", + "VET", + "VIA", + "VIB", + "VIBE", + "WABI", + "WAN", + "WAVES", + "WINGS", + "WPR", + "XEM", + "XLM", + "XMR", + "XRP", + "XZC", + "YOYO", + "ZEC", + "ZEN", + "ZIL" + ], + "currencies": [ + "BNB", + "BTC", + "ETH", + "USDT", + "TUSD", + "PAX", + "USDC", + "XRP" + ], + "markets": [ + { + "pair": [ + "BNB", + "NULS" + ], + "minimalOrder": { + "amount": 0.1, + "price": 0.00001, + "order": 1 + } + }, + { + "pair": [ + "BTC", + "BNB" + ], + "minimalOrder": { + "amount": 0.01, + "price": 1e-7, + "order": 0.001 + } + }, + { + "pair": [ + "BTC", + "NULS" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "BTC", + "NEO" + ], + "minimalOrder": { + "amount": 0.01, + "price": 0.000001, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "NULS" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.01 + } + }, + { + "pair": [ + "BTC", + "LINK" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "BTC", + "SALT" + ], + "minimalOrder": { + "amount": 0.01, + "price": 1e-7, + "order": 0.001 + } + }, + { + "pair": [ + "BTC", + "IOTA" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "BTC", + "ETC" + ], + "minimalOrder": { + "amount": 0.01, + "price": 0.000001, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "AST" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-7, + "order": 0.01 + } + }, + { + "pair": [ + "BTC", + "KNC" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "BTC", + "WTC" + ], + "minimalOrder": { + "amount": 0.01, + "price": 1e-7, + "order": 0.001 + } + }, + { + "pair": [ + "BTC", + "SNGLS" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "EOS" + ], + "minimalOrder": { + "amount": 0.01, + "price": 0.000001, + "order": 0.01 + } + }, + { + "pair": [ + "ETH", + "SNT" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.01 + } + }, + { + "pair": [ + "ETH", + "MCO" + ], + "minimalOrder": { + "amount": 0.01, + "price": 0.000001, + "order": 0.01 + } + }, + { + "pair": [ + "USDT", + "BTC" + ], + "minimalOrder": { + "amount": 0.000001, + "price": 0.01, + "order": 10 + } + }, + { + "pair": [ + "ETH", + "OAX" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-7, + "order": 0.01 + } + }, + { + "pair": [ + "ETH", + "OMG" + ], + "minimalOrder": { + "amount": 0.01, + "price": 0.000001, + "order": 0.01 + } + }, + { + "pair": [ + "BTC", + "GAS" + ], + "minimalOrder": { + "amount": 0.01, + "price": 0.000001, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "BQX" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-7, + "order": 0.01 + } + }, + { + "pair": [ + "ETH", + "WTC" + ], + "minimalOrder": { + "amount": 0.01, + "price": 0.000001, + "order": 0.01 + } + }, + { + "pair": [ + "ETH", + "QTUM" + ], + "minimalOrder": { + "amount": 0.01, + "price": 0.000001, + "order": 0.01 + } + }, + { + "pair": [ + "ETH", + "BNT" + ], + "minimalOrder": { + "amount": 0.01, + "price": 0.000001, + "order": 0.01 + } + }, + { + "pair": [ + "ETH", + "DNT" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.01 + } + }, + { + "pair": [ + "BTC", + "SNM" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "SNM" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.01 + } + }, + { + "pair": [ + "ETH", + "SNGLS" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.01 + } + }, + { + "pair": [ + "BTC", + "BQX" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "NEO" + ], + "minimalOrder": { + "amount": 0.01, + "price": 0.000001, + "order": 0.01 + } + }, + { + "pair": [ + "ETH", + "KNC" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-7, + "order": 0.01 + } + }, + { + "pair": [ + "ETH", + "STRAT" + ], + "minimalOrder": { + "amount": 0.01, + "price": 0.000001, + "order": 0.01 + } + }, + { + "pair": [ + "ETH", + "ZRX" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.01 + } + }, + { + "pair": [ + "BTC", + "QTUM" + ], + "minimalOrder": { + "amount": 0.01, + "price": 0.000001, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "FUN" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.01 + } + }, + { + "pair": [ + "BTC", + "LTC" + ], + "minimalOrder": { + "amount": 0.01, + "price": 0.000001, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "LINK" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.01 + } + }, + { + "pair": [ + "BTC", + "ETH" + ], + "minimalOrder": { + "amount": 0.001, + "price": 0.000001, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "XVG" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.01 + } + }, + { + "pair": [ + "BTC", + "STRAT" + ], + "minimalOrder": { + "amount": 0.01, + "price": 1e-7, + "order": 0.001 + } + }, + { + "pair": [ + "BTC", + "ZRX" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "IOTA" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.01 + } + }, + { + "pair": [ + "BTC", + "OMG" + ], + "minimalOrder": { + "amount": 0.01, + "price": 0.000001, + "order": 0.001 + } + }, + { + "pair": [ + "BTC", + "MCO" + ], + "minimalOrder": { + "amount": 0.01, + "price": 0.000001, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "SALT" + ], + "minimalOrder": { + "amount": 0.01, + "price": 0.000001, + "order": 0.01 + } + }, + { + "pair": [ + "BNB", + "ADA" + ], + "minimalOrder": { + "amount": 0.1, + "price": 0.00001, + "order": 1 + } + }, + { + "pair": [ + "BTC", + "ADA" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "ADA" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.01 + } + }, + { + "pair": [ + "TUSD", + "ADA" + ], + "minimalOrder": { + "amount": 0.1, + "price": 0.00001, + "order": 1 + } + }, + { + "pair": [ + "USDT", + "ADA" + ], + "minimalOrder": { + "amount": 0.1, + "price": 0.00001, + "order": 10 + } + }, + { + "pair": [ + "BNB", + "ADX" + ], + "minimalOrder": { + "amount": 0.1, + "price": 0.00001, + "order": 1 + } + }, + { + "pair": [ + "BTC", + "ADX" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "ADX" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-7, + "order": 0.01 + } + }, + { + "pair": [ + "BNB", + "AE" + ], + "minimalOrder": { + "amount": 0.1, + "price": 0.00001, + "order": 1 + } + }, + { + "pair": [ + "BTC", + "AE" + ], + "minimalOrder": { + "amount": 0.01, + "price": 1e-7, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "AE" + ], + "minimalOrder": { + "amount": 0.01, + "price": 0.000001, + "order": 0.01 + } + }, + { + "pair": [ + "BNB", + "AGI" + ], + "minimalOrder": { + "amount": 0.1, + "price": 0.00001, + "order": 1 + } + }, + { + "pair": [ + "BTC", + "AGI" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "AGI" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.01 + } + }, + { + "pair": [ + "BNB", + "AION" + ], + "minimalOrder": { + "amount": 0.1, + "price": 0.00001, + "order": 1 + } + }, + { + "pair": [ + "BTC", + "AION" + ], + "minimalOrder": { + "amount": 0.01, + "price": 1e-7, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "AION" + ], + "minimalOrder": { + "amount": 0.01, + "price": 0.000001, + "order": 0.01 + } + }, + { + "pair": [ + "BNB", + "AMB" + ], + "minimalOrder": { + "amount": 0.1, + "price": 0.00001, + "order": 1 + } + }, + { + "pair": [ + "BTC", + "AMB" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "AMB" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.01 + } + }, + { + "pair": [ + "BNB", + "APPC" + ], + "minimalOrder": { + "amount": 0.1, + "price": 0.00001, + "order": 1 + } + }, + { + "pair": [ + "BTC", + "APPC" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "APPC" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-7, + "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", + "ARK" + ], + "minimalOrder": { + "amount": 0.01, + "price": 1e-7, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "ARK" + ], + "minimalOrder": { + "amount": 0.01, + "price": 0.000001, + "order": 0.01 + } + }, + { + "pair": [ + "BTC", + "ARN" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "ARN" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.01 + } + }, + { + "pair": [ + "BTC", + "AST" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "BNB", + "BAT" + ], + "minimalOrder": { + "amount": 0.1, + "price": 0.00001, + "order": 1 + } + }, + { + "pair": [ + "BTC", + "BAT" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "BAT" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.01 + } + }, + { + "pair": [ + "BTC", + "BCD" + ], + "minimalOrder": { + "amount": 0.001, + "price": 0.000001, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "BCD" + ], + "minimalOrder": { + "amount": 0.001, + "price": 0.00001, + "order": 0.01 + } + }, + { + "pair": [ + "BTC", + "BCHABC" + ], + "minimalOrder": { + "amount": 0.001, + "price": 0.000001, + "order": 0.001 + } + }, + { + "pair": [ + "USDT", + "BCHABC" + ], + "minimalOrder": { + "amount": 0.00001, + "price": 0.01, + "order": 10 + } + }, + { + "pair": [ + "BTC", + "BCHSV" + ], + "minimalOrder": { + "amount": 0.001, + "price": 0.000001, + "order": 0.001 + } + }, + { + "pair": [ + "USDT", + "BCHSV" + ], + "minimalOrder": { + "amount": 0.00001, + "price": 0.01, + "order": 10 + } + }, + { + "pair": [ + "BNB", + "BCPT" + ], + "minimalOrder": { + "amount": 0.1, + "price": 0.00001, + "order": 1 + } + }, + { + "pair": [ + "BTC", + "BCPT" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "BCPT" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.01 + } + }, + { + "pair": [ + "BNB", + "BLZ" + ], + "minimalOrder": { + "amount": 0.1, + "price": 0.00001, + "order": 1 + } + }, + { + "pair": [ + "BTC", + "BLZ" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "BLZ" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.01 + } + }, + { + "pair": [ + "ETH", + "BNB" + ], + "minimalOrder": { + "amount": 0.01, + "price": 0.000001, + "order": 0.01 + } + }, + { + "pair": [ + "PAX", + "BNB" + ], + "minimalOrder": { + "amount": 0.01, + "price": 0.0001, + "order": 1 + } + }, + { + "pair": [ + "USDC", + "BNB" + ], + "minimalOrder": { + "amount": 0.01, + "price": 0.0001, + "order": 1 + } + }, + { + "pair": [ + "BTC", + "BNT" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "BNB", + "BRD" + ], + "minimalOrder": { + "amount": 0.1, + "price": 0.00001, + "order": 1 + } + }, + { + "pair": [ + "BTC", + "BRD" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "BRD" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-7, + "order": 0.01 + } + }, + { + "pair": [ + "PAX", + "BTC" + ], + "minimalOrder": { + "amount": 0.000001, + "price": 0.01, + "order": 1 + } + }, + { + "pair": [ + "USDC", + "BTC" + ], + "minimalOrder": { + "amount": 0.000001, + "price": 0.01, + "order": 1 + } + }, + { + "pair": [ + "BTC", + "BTG" + ], + "minimalOrder": { + "amount": 0.01, + "price": 0.000001, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "BTG" + ], + "minimalOrder": { + "amount": 0.01, + "price": 0.000001, + "order": 0.01 + } + }, + { + "pair": [ + "BNB", + "BTS" + ], + "minimalOrder": { + "amount": 0.1, + "price": 0.00001, + "order": 1 + } + }, + { + "pair": [ + "BTC", + "BTS" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "BTS" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.01 + } + }, + { + "pair": [ + "BTC", + "CDT" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "CDT" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.01 + } + }, + { + "pair": [ + "BTC", + "CLOAK" + ], + "minimalOrder": { + "amount": 0.01, + "price": 1e-7, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "CLOAK" + ], + "minimalOrder": { + "amount": 0.01, + "price": 0.000001, + "order": 0.01 + } + }, + { + "pair": [ + "BNB", + "CMT" + ], + "minimalOrder": { + "amount": 0.1, + "price": 0.00001, + "order": 1 + } + }, + { + "pair": [ + "BTC", + "CMT" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "CMT" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.01 + } + }, + { + "pair": [ + "BNB", + "CND" + ], + "minimalOrder": { + "amount": 1, + "price": 0.000001, + "order": 1 + } + }, + { + "pair": [ + "BTC", + "CND" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "CND" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.01 + } + }, + { + "pair": [ + "BNB", + "CVC" + ], + "minimalOrder": { + "amount": 0.1, + "price": 0.00001, + "order": 1 + } + }, + { + "pair": [ + "BTC", + "CVC" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "CVC" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.01 + } + }, + { + "pair": [ + "BTC", + "DASH" + ], + "minimalOrder": { + "amount": 0.001, + "price": 0.000001, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "DASH" + ], + "minimalOrder": { + "amount": 0.001, + "price": 0.00001, + "order": 0.01 + } + }, + { + "pair": [ + "BTC", + "DATA" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "DATA" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.01 + } + }, + { + "pair": [ + "BNB", + "DCR" + ], + "minimalOrder": { + "amount": 0.001, + "price": 0.001, + "order": 1 + } + }, + { + "pair": [ + "BTC", + "DCR" + ], + "minimalOrder": { + "amount": 0.001, + "price": 0.000001, + "order": 0.001 + } + }, + { + "pair": [ + "BTC", + "DENT" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "DENT" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.01 + } + }, + { + "pair": [ + "BTC", + "DGD" + ], + "minimalOrder": { + "amount": 0.001, + "price": 0.000001, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "DGD" + ], + "minimalOrder": { + "amount": 0.001, + "price": 0.00001, + "order": 0.01 + } + }, + { + "pair": [ + "BNB", + "DLT" + ], + "minimalOrder": { + "amount": 0.1, + "price": 0.00001, + "order": 1 + } + }, + { + "pair": [ + "BTC", + "DLT" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "DLT" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.01 + } + }, + { + "pair": [ + "BTC", + "DNT" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "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", + "EDO" + ], + "minimalOrder": { + "amount": 0.01, + "price": 1e-7, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "EDO" + ], + "minimalOrder": { + "amount": 0.01, + "price": 0.000001, + "order": 0.01 + } + }, + { + "pair": [ + "BTC", + "ELF" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "ELF" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.01 + } + }, + { + "pair": [ + "BTC", + "ENG" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "ENG" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-7, + "order": 0.01 + } + }, + { + "pair": [ + "BNB", + "ENJ" + ], + "minimalOrder": { + "amount": 1, + "price": 0.000001, + "order": 1 + } + }, + { + "pair": [ + "BTC", + "ENJ" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "ENJ" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.01 + } + }, + { + "pair": [ + "BNB", + "EOS" + ], + "minimalOrder": { + "amount": 0.01, + "price": 0.0001, + "order": 1 + } + }, + { + "pair": [ + "BTC", + "EOS" + ], + "minimalOrder": { + "amount": 0.01, + "price": 1e-7, + "order": 0.001 + } + }, + { + "pair": [ + "PAX", + "EOS" + ], + "minimalOrder": { + "amount": 0.01, + "price": 0.0001, + "order": 1 + } + }, + { + "pair": [ + "TUSD", + "EOS" + ], + "minimalOrder": { + "amount": 0.01, + "price": 0.0001, + "order": 1 + } + }, + { + "pair": [ + "USDC", + "EOS" + ], + "minimalOrder": { + "amount": 0.01, + "price": 0.0001, + "order": 1 + } + }, + { + "pair": [ + "USDT", + "EOS" + ], + "minimalOrder": { + "amount": 0.01, + "price": 0.0001, + "order": 10 + } + }, + { + "pair": [ + "BNB", + "ETC" + ], + "minimalOrder": { + "amount": 0.01, + "price": 0.0001, + "order": 1 + } + }, + { + "pair": [ + "ETH", + "ETC" + ], + "minimalOrder": { + "amount": 0.01, + "price": 0.000001, + "order": 0.01 + } + }, + { + "pair": [ + "USDT", + "ETC" + ], + "minimalOrder": { + "amount": 0.01, + "price": 0.0001, + "order": 10 + } + }, + { + "pair": [ + "PAX", + "ETH" + ], + "minimalOrder": { + "amount": 0.00001, + "price": 0.01, + "order": 1 + } + }, + { + "pair": [ + "USDC", + "ETH" + ], + "minimalOrder": { + "amount": 0.00001, + "price": 0.01, + "order": 1 + } + }, + { + "pair": [ + "BTC", + "EVX" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "EVX" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-7, + "order": 0.01 + } + }, + { + "pair": [ + "BTC", + "FUEL" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "FUEL" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.01 + } + }, + { + "pair": [ + "BTC", + "FUN" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "BNB", + "GNT" + ], + "minimalOrder": { + "amount": 0.1, + "price": 0.00001, + "order": 1 + } + }, + { + "pair": [ + "BTC", + "GNT" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "GNT" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.01 + } + }, + { + "pair": [ + "BNB", + "GO" + ], + "minimalOrder": { + "amount": 1, + "price": 0.000001, + "order": 1 + } + }, + { + "pair": [ + "BTC", + "GO" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "BTC", + "GRS" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "GRS" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.01 + } + }, + { + "pair": [ + "BNB", + "GTO" + ], + "minimalOrder": { + "amount": 0.1, + "price": 0.00001, + "order": 1 + } + }, + { + "pair": [ + "BTC", + "GTO" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "GTO" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.01 + } + }, + { + "pair": [ + "BTC", + "GVT" + ], + "minimalOrder": { + "amount": 0.01, + "price": 1e-7, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "GVT" + ], + "minimalOrder": { + "amount": 0.01, + "price": 0.000001, + "order": 0.01 + } + }, + { + "pair": [ + "BTC", + "GXS" + ], + "minimalOrder": { + "amount": 0.01, + "price": 1e-7, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "GXS" + ], + "minimalOrder": { + "amount": 0.01, + "price": 0.000001, + "order": 0.01 + } + }, + { + "pair": [ + "BTC", + "HC" + ], + "minimalOrder": { + "amount": 0.01, + "price": 1e-7, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "HC" + ], + "minimalOrder": { + "amount": 0.01, + "price": 0.000001, + "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": [ + "BNB", + "ICX" + ], + "minimalOrder": { + "amount": 0.1, + "price": 0.00001, + "order": 1 + } + }, + { + "pair": [ + "BTC", + "ICX" + ], + "minimalOrder": { + "amount": 0.01, + "price": 1e-7, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "ICX" + ], + "minimalOrder": { + "amount": 0.01, + "price": 0.000001, + "order": 0.01 + } + }, + { + "pair": [ + "USDT", + "ICX" + ], + "minimalOrder": { + "amount": 0.01, + "price": 0.0001, + "order": 10 + } + }, + { + "pair": [ + "BTC", + "INS" + ], + "minimalOrder": { + "amount": 0.01, + "price": 1e-7, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "INS" + ], + "minimalOrder": { + "amount": 0.01, + "price": 0.000001, + "order": 0.01 + } + }, + { + "pair": [ + "BTC", + "IOST" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "IOST" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.01 + } + }, + { + "pair": [ + "BNB", + "IOTA" + ], + "minimalOrder": { + "amount": 0.1, + "price": 0.00001, + "order": 1 + } + }, + { + "pair": [ + "USDT", + "IOTA" + ], + "minimalOrder": { + "amount": 0.01, + "price": 0.0001, + "order": 10 + } + }, + { + "pair": [ + "BTC", + "IOTX" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "IOTX" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.01 + } + }, + { + "pair": [ + "BTC", + "KEY" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "KEY" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.01 + } + }, + { + "pair": [ + "BTC", + "KMD" + ], + "minimalOrder": { + "amount": 0.01, + "price": 1e-7, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "KMD" + ], + "minimalOrder": { + "amount": 0.01, + "price": 0.000001, + "order": 0.01 + } + }, + { + "pair": [ + "BTC", + "LEND" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "LEND" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.01 + } + }, + { + "pair": [ + "BNB", + "LOOM" + ], + "minimalOrder": { + "amount": 0.1, + "price": 0.00001, + "order": 1 + } + }, + { + "pair": [ + "BTC", + "LOOM" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "LOOM" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.01 + } + }, + { + "pair": [ + "BTC", + "LRC" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "LRC" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.01 + } + }, + { + "pair": [ + "BNB", + "LSK" + ], + "minimalOrder": { + "amount": 0.01, + "price": 0.0001, + "order": 1 + } + }, + { + "pair": [ + "BTC", + "LSK" + ], + "minimalOrder": { + "amount": 0.01, + "price": 1e-7, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "LSK" + ], + "minimalOrder": { + "amount": 0.01, + "price": 0.000001, + "order": 0.01 + } + }, + { + "pair": [ + "BNB", + "LTC" + ], + "minimalOrder": { + "amount": 0.00001, + "price": 0.01, + "order": 1 + } + }, + { + "pair": [ + "ETH", + "LTC" + ], + "minimalOrder": { + "amount": 0.001, + "price": 0.00001, + "order": 0.01 + } + }, + { + "pair": [ + "USDT", + "LTC" + ], + "minimalOrder": { + "amount": 0.00001, + "price": 0.01, + "order": 10 + } + }, + { + "pair": [ + "BTC", + "LUN" + ], + "minimalOrder": { + "amount": 0.01, + "price": 1e-7, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "LUN" + ], + "minimalOrder": { + "amount": 0.01, + "price": 0.000001, + "order": 0.01 + } + }, + { + "pair": [ + "BTC", + "MANA" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "MANA" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.01 + } + }, + { + "pair": [ + "BNB", + "MCO" + ], + "minimalOrder": { + "amount": 0.1, + "price": 0.00001, + "order": 1 + } + }, + { + "pair": [ + "BTC", + "MDA" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "MDA" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-7, + "order": 0.01 + } + }, + { + "pair": [ + "BNB", + "MFT" + ], + "minimalOrder": { + "amount": 1, + "price": 0.000001, + "order": 1 + } + }, + { + "pair": [ + "BTC", + "MFT" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "MFT" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.01 + } + }, + { + "pair": [ + "BNB", + "MITH" + ], + "minimalOrder": { + "amount": 0.1, + "price": 0.00001, + "order": 1 + } + }, + { + "pair": [ + "BTC", + "MITH" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "BTC", + "MOD" + ], + "minimalOrder": { + "amount": 0.01, + "price": 1e-7, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "MOD" + ], + "minimalOrder": { + "amount": 0.01, + "price": 0.000001, + "order": 0.01 + } + }, + { + "pair": [ + "BTC", + "MTH" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "MTH" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.01 + } + }, + { + "pair": [ + "BTC", + "MTL" + ], + "minimalOrder": { + "amount": 0.01, + "price": 1e-7, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "MTL" + ], + "minimalOrder": { + "amount": 0.01, + "price": 0.000001, + "order": 0.01 + } + }, + { + "pair": [ + "BNB", + "NANO" + ], + "minimalOrder": { + "amount": 0.01, + "price": 0.0001, + "order": 1 + } + }, + { + "pair": [ + "BTC", + "NANO" + ], + "minimalOrder": { + "amount": 0.01, + "price": 1e-7, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "NANO" + ], + "minimalOrder": { + "amount": 0.01, + "price": 0.000001, + "order": 0.01 + } + }, + { + "pair": [ + "BNB", + "NAS" + ], + "minimalOrder": { + "amount": 0.1, + "price": 0.00001, + "order": 1 + } + }, + { + "pair": [ + "BTC", + "NAS" + ], + "minimalOrder": { + "amount": 0.01, + "price": 1e-7, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "NAS" + ], + "minimalOrder": { + "amount": 0.01, + "price": 0.000001, + "order": 0.01 + } + }, + { + "pair": [ + "BNB", + "NAV" + ], + "minimalOrder": { + "amount": 0.1, + "price": 0.00001, + "order": 1 + } + }, + { + "pair": [ + "BTC", + "NAV" + ], + "minimalOrder": { + "amount": 0.01, + "price": 1e-7, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "NAV" + ], + "minimalOrder": { + "amount": 0.01, + "price": 0.000001, + "order": 0.01 + } + }, + { + "pair": [ + "BNB", + "NCASH" + ], + "minimalOrder": { + "amount": 1, + "price": 0.000001, + "order": 1 + } + }, + { + "pair": [ + "BTC", + "NCASH" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "NCASH" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.01 + } + }, + { + "pair": [ + "BNB", + "NEBL" + ], + "minimalOrder": { + "amount": 0.1, + "price": 0.00001, + "order": 1 + } + }, + { + "pair": [ + "BTC", + "NEBL" + ], + "minimalOrder": { + "amount": 0.01, + "price": 1e-7, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "NEBL" + ], + "minimalOrder": { + "amount": 0.01, + "price": 0.000001, + "order": 0.01 + } + }, + { + "pair": [ + "BNB", + "NEO" + ], + "minimalOrder": { + "amount": 0.001, + "price": 0.001, + "order": 1 + } + }, + { + "pair": [ + "TUSD", + "NEO" + ], + "minimalOrder": { + "amount": 0.001, + "price": 0.001, + "order": 1 + } + }, + { + "pair": [ + "USDT", + "NEO" + ], + "minimalOrder": { + "amount": 0.001, + "price": 0.001, + "order": 10 + } + }, + { + "pair": [ + "BTC", + "NPXS" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "NPXS" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.01 + } + }, + { + "pair": [ + "USDT", + "NULS" + ], + "minimalOrder": { + "amount": 0.01, + "price": 0.0001, + "order": 10 + } + }, + { + "pair": [ + "BNB", + "NXS" + ], + "minimalOrder": { + "amount": 0.01, + "price": 0.0001, + "order": 1 + } + }, + { + "pair": [ + "BTC", + "NXS" + ], + "minimalOrder": { + "amount": 0.01, + "price": 1e-7, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "NXS" + ], + "minimalOrder": { + "amount": 0.01, + "price": 0.000001, + "order": 0.01 + } + }, + { + "pair": [ + "BTC", + "OAX" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "BNB", + "ONT" + ], + "minimalOrder": { + "amount": 0.1, + "price": 0.00001, + "order": 1 + } + }, + { + "pair": [ + "BTC", + "ONT" + ], + "minimalOrder": { + "amount": 0.01, + "price": 1e-7, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "ONT" + ], + "minimalOrder": { + "amount": 0.01, + "price": 0.000001, + "order": 0.01 + } + }, + { + "pair": [ + "USDT", + "ONT" + ], + "minimalOrder": { + "amount": 0.01, + "price": 0.0001, + "order": 10 + } + }, + { + "pair": [ + "BNB", + "OST" + ], + "minimalOrder": { + "amount": 1, + "price": 0.000001, + "order": 1 + } + }, + { + "pair": [ + "BTC", + "OST" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "OST" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.01 + } + }, + { + "pair": [ + "USDT", + "PAX" + ], + "minimalOrder": { + "amount": 0.01, + "price": 0.0001, + "order": 10 + } + }, + { + "pair": [ + "BNB", + "PHX" + ], + "minimalOrder": { + "amount": 1, + "price": 0.000001, + "order": 1 + } + }, + { + "pair": [ + "BTC", + "PHX" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "PHX" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.01 + } + }, + { + "pair": [ + "BNB", + "PIVX" + ], + "minimalOrder": { + "amount": 0.1, + "price": 0.00001, + "order": 1 + } + }, + { + "pair": [ + "BTC", + "PIVX" + ], + "minimalOrder": { + "amount": 0.01, + "price": 1e-7, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "PIVX" + ], + "minimalOrder": { + "amount": 0.01, + "price": 0.000001, + "order": 0.01 + } + }, + { + "pair": [ + "BNB", + "POA" + ], + "minimalOrder": { + "amount": 0.1, + "price": 0.00001, + "order": 1 + } + }, + { + "pair": [ + "BTC", + "POA" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "POA" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.01 + } + }, + { + "pair": [ + "BTC", + "POE" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "POE" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "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.1, + "price": 0.00001, + "order": 1 + } + }, + { + "pair": [ + "BTC", + "POWR" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "POWR" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.01 + } + }, + { + "pair": [ + "BTC", + "PPT" + ], + "minimalOrder": { + "amount": 0.01, + "price": 1e-7, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "PPT" + ], + "minimalOrder": { + "amount": 0.01, + "price": 0.000001, + "order": 0.01 + } + }, + { + "pair": [ + "BTC", + "QKC" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "QKC" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.01 + } + }, + { + "pair": [ + "BNB", + "QLC" + ], + "minimalOrder": { + "amount": 1, + "price": 0.000001, + "order": 1 + } + }, + { + "pair": [ + "BTC", + "QLC" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "QLC" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.01 + } + }, + { + "pair": [ + "BNB", + "QSP" + ], + "minimalOrder": { + "amount": 1, + "price": 0.000001, + "order": 1 + } + }, + { + "pair": [ + "BTC", + "QSP" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "QSP" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.01 + } + }, + { + "pair": [ + "BNB", + "QTUM" + ], + "minimalOrder": { + "amount": 0.1, + "price": 0.00001, + "order": 1 + } + }, + { + "pair": [ + "USDT", + "QTUM" + ], + "minimalOrder": { + "amount": 0.001, + "price": 0.001, + "order": 10 + } + }, + { + "pair": [ + "BNB", + "RCN" + ], + "minimalOrder": { + "amount": 1, + "price": 0.000001, + "order": 1 + } + }, + { + "pair": [ + "BTC", + "RCN" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "RCN" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.01 + } + }, + { + "pair": [ + "BNB", + "RDN" + ], + "minimalOrder": { + "amount": 0.1, + "price": 0.00001, + "order": 1 + } + }, + { + "pair": [ + "BTC", + "RDN" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "RDN" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-7, + "order": 0.01 + } + }, + { + "pair": [ + "BNB", + "REN" + ], + "minimalOrder": { + "amount": 0.1, + "price": 0.00001, + "order": 1 + } + }, + { + "pair": [ + "BTC", + "REN" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "BNB", + "REP" + ], + "minimalOrder": { + "amount": 0.001, + "price": 0.001, + "order": 1 + } + }, + { + "pair": [ + "BTC", + "REP" + ], + "minimalOrder": { + "amount": 0.001, + "price": 0.000001, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "REP" + ], + "minimalOrder": { + "amount": 0.001, + "price": 0.00001, + "order": 0.01 + } + }, + { + "pair": [ + "BTC", + "REQ" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "REQ" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.01 + } + }, + { + "pair": [ + "BNB", + "RLC" + ], + "minimalOrder": { + "amount": 0.1, + "price": 0.00001, + "order": 1 + } + }, + { + "pair": [ + "BTC", + "RLC" + ], + "minimalOrder": { + "amount": 0.01, + "price": 1e-7, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "RLC" + ], + "minimalOrder": { + "amount": 0.01, + "price": 0.000001, + "order": 0.01 + } + }, + { + "pair": [ + "BNB", + "RVN" + ], + "minimalOrder": { + "amount": 1, + "price": 0.000001, + "order": 1 + } + }, + { + "pair": [ + "BTC", + "RVN" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "BNB", + "SC" + ], + "minimalOrder": { + "amount": 1, + "price": 0.000001, + "order": 1 + } + }, + { + "pair": [ + "BTC", + "SC" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "SC" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.01 + } + }, + { + "pair": [ + "BNB", + "SKY" + ], + "minimalOrder": { + "amount": 0.001, + "price": 0.001, + "order": 1 + } + }, + { + "pair": [ + "BTC", + "SKY" + ], + "minimalOrder": { + "amount": 0.001, + "price": 0.000001, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "SKY" + ], + "minimalOrder": { + "amount": 0.001, + "price": 0.00001, + "order": 0.01 + } + }, + { + "pair": [ + "BTC", + "SNT" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "BNB", + "STEEM" + ], + "minimalOrder": { + "amount": 0.1, + "price": 0.00001, + "order": 1 + } + }, + { + "pair": [ + "BTC", + "STEEM" + ], + "minimalOrder": { + "amount": 0.01, + "price": 1e-7, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "STEEM" + ], + "minimalOrder": { + "amount": 0.01, + "price": 0.000001, + "order": 0.01 + } + }, + { + "pair": [ + "BTC", + "STORJ" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "STORJ" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-7, + "order": 0.01 + } + }, + { + "pair": [ + "BNB", + "STORM" + ], + "minimalOrder": { + "amount": 1, + "price": 0.000001, + "order": 1 + } + }, + { + "pair": [ + "BTC", + "STORM" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "STORM" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.01 + } + }, + { + "pair": [ + "BTC", + "SUB" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "SUB" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.01 + } + }, + { + "pair": [ + "BNB", + "SYS" + ], + "minimalOrder": { + "amount": 0.1, + "price": 0.00001, + "order": 1 + } + }, + { + "pair": [ + "BTC", + "SYS" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "SYS" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.01 + } + }, + { + "pair": [ + "BNB", + "THETA" + ], + "minimalOrder": { + "amount": 0.1, + "price": 0.00001, + "order": 1 + } + }, + { + "pair": [ + "BTC", + "THETA" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "THETA" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.01 + } + }, + { + "pair": [ + "BTC", + "TNB" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "TNB" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.01 + } + }, + { + "pair": [ + "BTC", + "TNT" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "TNT" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.01 + } + }, + { + "pair": [ + "BNB", + "TRX" + ], + "minimalOrder": { + "amount": 1, + "price": 0.000001, + "order": 1 + } + }, + { + "pair": [ + "BTC", + "TRX" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "TRX" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.01 + } + }, + { + "pair": [ + "TUSD", + "TRX" + ], + "minimalOrder": { + "amount": 0.1, + "price": 0.00001, + "order": 1 + } + }, + { + "pair": [ + "USDT", + "TRX" + ], + "minimalOrder": { + "amount": 0.1, + "price": 0.00001, + "order": 10 + } + }, + { + "pair": [ + "XRP", + "TRX" + ], + "minimalOrder": { + "amount": 0.1, + "price": 0.00001, + "order": 1 + } + }, + { + "pair": [ + "BNB", + "TUSD" + ], + "minimalOrder": { + "amount": 0.1, + "price": 0.00001, + "order": 1 + } + }, + { + "pair": [ + "BTC", + "TUSD" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "TUSD" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.01 + } + }, + { + "pair": [ + "USDT", + "TUSD" + ], + "minimalOrder": { + "amount": 0.01, + "price": 0.0001, + "order": 10 + } + }, + { + "pair": [ + "USDT", + "USDC" + ], + "minimalOrder": { + "amount": 0.01, + "price": 0.0001, + "order": 10 + } + }, + { + "pair": [ + "BNB", + "VET" + ], + "minimalOrder": { + "amount": 0.1, + "price": 0.00001, + "order": 1 + } + }, + { + "pair": [ + "BTC", + "VET" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "VET" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.01 + } + }, + { + "pair": [ + "USDT", + "VET" + ], + "minimalOrder": { + "amount": 1, + "price": 0.000001, + "order": 10 + } + }, + { + "pair": [ + "BNB", + "VIA" + ], + "minimalOrder": { + "amount": 0.1, + "price": 0.00001, + "order": 1 + } + }, + { + "pair": [ + "BTC", + "VIA" + ], + "minimalOrder": { + "amount": 0.01, + "price": 1e-7, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "VIA" + ], + "minimalOrder": { + "amount": 0.01, + "price": 0.000001, + "order": 0.01 + } + }, + { + "pair": [ + "BTC", + "VIB" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "BTC", + "VIBE" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "VIBE" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-7, + "order": 0.01 + } + }, + { + "pair": [ + "ETH", + "VIB" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.01 + } + }, + { + "pair": [ + "BNB", + "WABI" + ], + "minimalOrder": { + "amount": 0.1, + "price": 0.00001, + "order": 1 + } + }, + { + "pair": [ + "BTC", + "WABI" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "WABI" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.01 + } + }, + { + "pair": [ + "BNB", + "WAN" + ], + "minimalOrder": { + "amount": 0.1, + "price": 0.00001, + "order": 1 + } + }, + { + "pair": [ + "BTC", + "WAN" + ], + "minimalOrder": { + "amount": 0.01, + "price": 1e-7, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "WAN" + ], + "minimalOrder": { + "amount": 0.01, + "price": 0.000001, + "order": 0.01 + } + }, + { + "pair": [ + "BNB", + "WAVES" + ], + "minimalOrder": { + "amount": 0.01, + "price": 0.0001, + "order": 1 + } + }, + { + "pair": [ + "BTC", + "WAVES" + ], + "minimalOrder": { + "amount": 0.01, + "price": 1e-7, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "WAVES" + ], + "minimalOrder": { + "amount": 0.01, + "price": 0.000001, + "order": 0.01 + } + }, + { + "pair": [ + "BTC", + "WINGS" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "WINGS" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-7, + "order": 0.01 + } + }, + { + "pair": [ + "BTC", + "WPR" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "WPR" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.01 + } + }, + { + "pair": [ + "BNB", + "WTC" + ], + "minimalOrder": { + "amount": 0.01, + "price": 0.0001, + "order": 1 + } + }, + { + "pair": [ + "BNB", + "XEM" + ], + "minimalOrder": { + "amount": 0.1, + "price": 0.00001, + "order": 1 + } + }, + { + "pair": [ + "BTC", + "XEM" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "XEM" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.01 + } + }, + { + "pair": [ + "BNB", + "XLM" + ], + "minimalOrder": { + "amount": 0.1, + "price": 0.00001, + "order": 1 + } + }, + { + "pair": [ + "BTC", + "XLM" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "XLM" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.01 + } + }, + { + "pair": [ + "PAX", + "XLM" + ], + "minimalOrder": { + "amount": 0.1, + "price": 0.00001, + "order": 1 + } + }, + { + "pair": [ + "TUSD", + "XLM" + ], + "minimalOrder": { + "amount": 0.1, + "price": 0.00001, + "order": 1 + } + }, + { + "pair": [ + "USDC", + "XLM" + ], + "minimalOrder": { + "amount": 0.1, + "price": 0.00001, + "order": 1 + } + }, + { + "pair": [ + "USDT", + "XLM" + ], + "minimalOrder": { + "amount": 0.1, + "price": 0.00001, + "order": 10 + } + }, + { + "pair": [ + "BTC", + "XMR" + ], + "minimalOrder": { + "amount": 0.001, + "price": 0.000001, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "XMR" + ], + "minimalOrder": { + "amount": 0.001, + "price": 0.00001, + "order": 0.01 + } + }, + { + "pair": [ + "BNB", + "XRP" + ], + "minimalOrder": { + "amount": 0.1, + "price": 0.00001, + "order": 1 + } + }, + { + "pair": [ + "BTC", + "XRP" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "XRP" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.01 + } + }, + { + "pair": [ + "PAX", + "XRP" + ], + "minimalOrder": { + "amount": 0.1, + "price": 0.00001, + "order": 1 + } + }, + { + "pair": [ + "TUSD", + "XRP" + ], + "minimalOrder": { + "amount": 0.1, + "price": 0.00001, + "order": 1 + } + }, + { + "pair": [ + "USDC", + "XRP" + ], + "minimalOrder": { + "amount": 0.1, + "price": 0.00001, + "order": 1 + } + }, + { + "pair": [ + "USDT", + "XRP" + ], + "minimalOrder": { + "amount": 0.1, + "price": 0.00001, + "order": 10 + } + }, + { + "pair": [ + "BTC", + "XVG" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "BNB", + "XZC" + ], + "minimalOrder": { + "amount": 0.001, + "price": 0.001, + "order": 1 + } + }, + { + "pair": [ + "BTC", + "XZC" + ], + "minimalOrder": { + "amount": 0.01, + "price": 0.000001, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "XZC" + ], + "minimalOrder": { + "amount": 0.01, + "price": 0.000001, + "order": 0.01 + } + }, + { + "pair": [ + "XRP", + "XZC" + ], + "minimalOrder": { + "amount": 0.001, + "price": 0.001, + "order": 1 + } + }, + { + "pair": [ + "BNB", + "YOYO" + ], + "minimalOrder": { + "amount": 1, + "price": 0.000001, + "order": 1 + } + }, + { + "pair": [ + "BTC", + "YOYO" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "YOYO" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.01 + } + }, + { + "pair": [ + "BTC", + "ZEC" + ], + "minimalOrder": { + "amount": 0.001, + "price": 0.000001, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "ZEC" + ], + "minimalOrder": { + "amount": 0.001, + "price": 0.00001, + "order": 0.01 + } + }, + { + "pair": [ + "BNB", + "ZEN" + ], + "minimalOrder": { + "amount": 0.001, + "price": 0.001, + "order": 1 + } + }, + { + "pair": [ + "BTC", + "ZEN" + ], + "minimalOrder": { + "amount": 0.001, + "price": 0.000001, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "ZEN" + ], + "minimalOrder": { + "amount": 0.001, + "price": 0.00001, + "order": 0.01 + } + }, + { + "pair": [ + "BNB", + "ZIL" + ], + "minimalOrder": { + "amount": 1, + "price": 0.000001, + "order": 1 + } + }, + { + "pair": [ + "BTC", + "ZIL" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "ZIL" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.01 + } + }, + { + "pair": [ + "USDT", + "BNB" + ], + "minimalOrder": { + "amount": 0.01, + "price": 0.0001, + "order": 10 + } + }, + { + "pair": [ + "USDT", + "ETH" + ], + "minimalOrder": { + "amount": 0.00001, + "price": 0.01, + "order": 10 + } + } + ] +} \ No newline at end of file diff --git a/exchange/wrappers/binance.js b/exchange/wrappers/binance.js new file mode 100644 index 0000000..2ec23c3 --- /dev/null +++ b/exchange/wrappers/binance.js @@ -0,0 +1,530 @@ +const moment = require('moment'); +const _ = require('lodash'); + +const Errors = require('../exchangeErrors'); +const marketData = require('./binance-markets.json'); +const exchangeUtils = require('../exchangeUtils'); +const retry = exchangeUtils.retry; +const scientificToDecimal = exchangeUtils.scientificToDecimal; + +const Binance = require('binance'); + +const Trader = function(config) { + _.bindAll(this, [ + 'roundAmount', + 'roundPrice', + 'isValidPrice', + 'isValidLot' + ]); + + if (_.isObject(config)) { + this.key = config.key; + this.secret = config.secret; + this.currency = config.currency.toUpperCase(); + 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'; + + this.market = _.find(Trader.getCapabilities().markets, (market) => { + return market.pair[0] === this.currency && market.pair[1] === this.asset + }); + + this.binance = new Binance.BinanceRest({ + key: this.key, + secret: this.secret, + timeout: 15000, + recvWindow, + disableBeautification: false, + handleDrift: true, + }); + + if(config.key && config.secret) { + // Note non standard func: + // + // On binance we might pay fees in BNB + // if we do we CANNOT calculate feePercent + // since we don't track BNB price (when we + // are not trading on a BNB market). + // + // Though we can deduce feePercent based + // on user fee tracked through `this.getFee`. + // Set default here, overwrite in getFee. + this.fee = 0.001; + // Set the proper fee asap. + this.getFee(_.noop); + + this.oldOrder = false; + } +}; + +const recoverableErrors = [ + 'SOCKETTIMEDOUT', + 'TIMEDOUT', + 'CONNRESET', + 'CONNREFUSED', + 'NOTFOUND', + 'Error -1021', + 'Response code 429', + 'Response code 5', + 'Response code 403', + 'ETIMEDOUT', + 'EHOSTUNREACH', + // getaddrinfo EAI_AGAIN api.binance.com api.binance.com:443 + 'EAI_AGAIN', + 'ENETUNREACH' +]; + +const includes = (str, list) => { + if(!_.isString(str)) + return false; + + return _.some(list, item => str.includes(item)); +} + +Trader.prototype.handleResponse = function(funcName, callback) { + return (error, body) => { + if (body && body.code) { + error = new Error(`Error ${body.code}: ${body.msg}`); + } + + if(error) { + if(_.isString(error)) { + error = new Error(error); + } + + if(includes(error.message, recoverableErrors)) { + error.notFatal = true; + } + + if(funcName === 'cancelOrder' && error.message.includes('UNKNOWN_ORDER')) { + console.log(new Date, 'cancelOrder', 'UNKNOWN_ORDER'); + // order got filled in full before it could be + // cancelled, meaning it was NOT cancelled. + return callback(false, {filled: true}); + } + + if(funcName === 'checkOrder' && error.message.includes('Order does not exist.')) { + console.log(new Date, 'Binance doesnt know this order, retrying up to 10 times..'); + error.retry = 10; + } + + if(funcName === 'addOrder' && error.message.includes('Account has insufficient balance')) { + console.log(new Date, 'insufficientFunds'); + error.type = 'insufficientFunds'; + } + + return callback(error); + } + + return callback(undefined, body); + } +}; + +Trader.prototype.getTrades = function(since, callback, descending) { + const processResults = (err, data) => { + if (err) return callback(err); + + var parsedTrades = []; + _.each( + data, + function(trade) { + parsedTrades.push({ + tid: trade.aggTradeId, + date: moment(trade.timestamp).unix(), + price: parseFloat(trade.price), + amount: parseFloat(trade.quantity), + }); + }, + this + ); + + if (descending) callback(null, parsedTrades.reverse()); + else callback(undefined, parsedTrades); + }; + + var reqData = { + symbol: this.pair, + }; + + if (since) { + var endTs = moment(since) + .add(1, 'h') + .valueOf(); + var nowTs = moment().valueOf(); + + reqData.startTime = moment(since).valueOf(); + reqData.endTime = endTs > nowTs ? nowTs : endTs; + } + + const fetch = cb => this.binance.aggTrades(reqData, this.handleResponse('getTrades', cb)); + retry(undefined, fetch, processResults); +}; + +Trader.prototype.getPortfolio = function(callback) { + const setBalance = (err, data) => { + if (err) return callback(err); + + const findAsset = item => item.asset === this.asset; + const assetAmount = parseFloat(_.find(data.balances, findAsset).free); + + const findCurrency = item => item.asset === this.currency; + const currencyAmount = parseFloat(_.find(data.balances, findCurrency).free); + + if (!_.isNumber(assetAmount) || _.isNaN(assetAmount)) { + assetAmount = 0; + } + + if (!_.isNumber(currencyAmount) || _.isNaN(currencyAmount)) { + currencyAmount = 0; + } + + const portfolio = [ + { name: this.asset, amount: assetAmount }, + { name: this.currency, amount: currencyAmount }, + ]; + + return callback(undefined, portfolio); + }; + + const fetch = cb => this.binance.account({}, this.handleResponse('getPortfolio', cb)); + retry(undefined, fetch, setBalance); +}; + +Trader.prototype.getFee = function(callback) { + + // binance does NOT tell us whether the user is using BNB to pay + // for fees, which means a discount (effectively lower fees) + const handle = (err, data) => { + if(err) { + return callback(err); + } + const basepoints = data.makerCommission; + + /** Binance raw response + { makerCommission: 10, + takerCommission: 10, + buyerCommission: 0, + sellerCommission: 0, + canTrade: true, + canWithdraw: true, + canDeposit: true, + So to get decimal representation of fee we actually need to divide by 10000 + */ + // note non standard func, see constructor + this.fee = basepoints / 10000; + + callback(undefined, this.fee); + } + + const fetch = cb => this.binance.account({}, this.handleResponse('getFee', cb)); + retry(undefined, fetch, handle); +}; + +Trader.prototype.getTicker = function(callback) { + const setTicker = (err, data) => { + if (err) + return callback(err); + + var result = _.find(data, ticker => ticker.symbol === this.pair); + + if(!result) + return callback(new Error(`Market ${this.pair} not found on Binance`)); + + var ticker = { + ask: parseFloat(result.askPrice), + bid: parseFloat(result.bidPrice), + }; + + callback(undefined, ticker); + }; + + const handler = cb => this.binance._makeRequest({}, this.handleResponse('getTicker', cb), 'api/v1/ticker/allBookTickers'); + retry(undefined, handler, setTicker); +}; + +// Effectively counts the number of decimal places, so 0.001 or 0.234 results in 3 +Trader.prototype.getPrecision = function(tickSize) { + if (!isFinite(tickSize)) return 0; + var e = 1, p = 0; + while (Math.round(tickSize * e) / e !== tickSize) { e *= 10; p++; } + return p; +}; + +Trader.prototype.round = function(amount, tickSize) { + var precision = 100000000; + var t = this.getPrecision(tickSize); + + if(Number.isInteger(t)) + precision = Math.pow(10, t); + + amount *= precision; + amount = Math.floor(amount); + amount /= precision; + + // https://gist.github.com/jiggzson/b5f489af9ad931e3d186 + amount = scientificToDecimal(amount); + + return amount; +}; + +Trader.prototype.roundAmount = function(amount) { + return this.round(amount, this.market.minimalOrder.amount); +} + +Trader.prototype.roundPrice = function(price) { + return this.round(price, this.market.minimalOrder.price); +} + +Trader.prototype.isValidPrice = function(price) { + return price >= this.market.minimalOrder.price; +} + +Trader.prototype.isValidLot = function(price, amount) { + return amount * price >= this.market.minimalOrder.order; +} + +Trader.prototype.outbidPrice = function(price, isUp) { + let newPrice; + + if(isUp) { + newPrice = price + this.market.minimalOrder.price; + } else { + newPrice = price - this.market.minimalOrder.price; + } + + return this.roundPrice(newPrice); +} + +Trader.prototype.addOrder = function(tradeType, amount, price, callback) { + const setOrder = (err, data) => { + if (err) return callback(err); + + const txid = data.orderId; + + callback(undefined, txid); + }; + + const reqData = { + symbol: this.pair, + side: tradeType.toUpperCase(), + type: 'LIMIT', + timeInForce: 'GTC', + quantity: amount, + price: price, + timestamp: new Date().getTime() + }; + + const handler = cb => this.binance.newOrder(reqData, this.handleResponse('addOrder', cb)); + retry(undefined, handler, setOrder); +}; + +Trader.prototype.getOpenOrders = function(callback) { + + const get = (err, data) => { + if(err) { + return callback(err); + } + + callback(null, data.map(o => o.orderId)); + } + + const reqData = { + symbol: this.pair + } + + const handler = cb => this.binance.openOrders(reqData, this.handleResponse('getOpenOrders', cb)); + retry(undefined, handler, get); +} + +Trader.prototype.getOrder = function(order, callback) { + const get = (err, data) => { + if (err) return callback(err); + + let price = 0; + let amount = 0; + let date = moment(0); + + const fees = {}; + + if(!data.length) { + return callback(new Error('Binance did not return any trades')); + } + + const trades = _.filter(data, t => { + // note: the API returns a string after creating + return t.orderId == order; + }); + + if(!trades.length) { + console.log('cannot find trades!', { order, list: data.map(t => t.orderId).reverse() }); + + const reqData = { + symbol: this.pair, + orderId: order, + }; + + this.binance.queryOrder(reqData, (err, resp) => { + console.log('couldnt find any trade for order, here is order:', {err, resp}); + + callback(new Error('Trades not found')); + }); + + return; + } + + _.each(trades, trade => { + date = moment(trade.time); + price = ((price * amount) + (+trade.price * trade.qty)) / (+trade.qty + amount); + amount += +trade.qty; + + if(fees[trade.commissionAsset]) + fees[trade.commissionAsset] += (+trade.commission); + else + fees[trade.commissionAsset] = (+trade.commission); + }); + + let feePercent; + if(_.keys(fees).length === 1) { + if(fees.BNB && this.asset !== 'BNB' && this.currency !== 'BNB') { + // we paid fees in BNB, right now that means the fee is always 75% + // of base fee. We cannot calculate since we do not have the BNB rate. + feePercent = this.fee * 0.75; + } else { + if(fees[this.asset]) { + feePercent = fees[this.asset] / amount * 100; + } else if(fees.currency) { + feePercent = fees[this.currency] / price / amount * 100; + } else { + // use user fee of 10 basepoints + feePercent = this.fee; + } + } + } else { + // we paid fees in multiple currencies? + // assume user fee + feePercent = this.fee; + } + + callback(undefined, { price, amount, date, fees, feePercent }); + } + + const reqData = { + symbol: this.pair, + // if this order was not part of the last 500 trades we won't find it.. + limit: 1000, + }; + + const handler = cb => this.binance.myTrades(reqData, this.handleResponse('getOrder', cb)); + retry(undefined, handler, get); +}; + +Trader.prototype.buy = function(amount, price, callback) { + this.addOrder('buy', amount, price, callback); +}; + +Trader.prototype.sell = function(amount, price, callback) { + this.addOrder('sell', amount, price, callback); +}; + +Trader.prototype.checkOrder = function(order, callback) { + + const check = (err, data) => { + if (err) { + return callback(err); + } + + if(data.filled === true) { + // binance responsed with order not found + return callback(undefined, { executed: true, open: false }); + } + + const status = data.status; + + if( + status === 'CANCELED' || + status === 'REJECTED' || + // for good measure: GB does not + // submit orders that can expire yet + status === 'EXPIRED' + ) { + return callback(undefined, { executed: false, open: false }); + } else if( + status === 'NEW' || + status === 'PARTIALLY_FILLED' + ) { + return callback(undefined, { executed: false, open: true, filledAmount: +data.executedQty }); + } else if(status === 'FILLED') { + return callback(undefined, { executed: true, open: false }) + } + + console.log('what status?', status); + throw status; + }; + + const reqData = { + symbol: this.pair, + orderId: order, + }; + + const fetcher = cb => this.binance.queryOrder(reqData, this.handleResponse('checkOrder', cb)); + retry(undefined, fetcher, check); +}; + +Trader.prototype.cancelOrder = function(order, callback) { + + const cancel = (err, data) => { + + this.oldOrder = order; + + if(err) { + return callback(err); + } + + if(data && data.filled) { + return callback(undefined, true); + } + + return callback(undefined, false); + }; + + let reqData = { + symbol: this.pair, + orderId: order, + }; + + const fetcher = cb => this.binance.cancelOrder(reqData, this.handleResponse('cancelOrder', cb)); + retry(undefined, fetcher, cancel); +}; + +Trader.getCapabilities = function() { + return { + name: 'Binance', + slug: 'binance', + currencies: marketData.currencies, + assets: marketData.assets, + markets: marketData.markets, + requires: ['key', 'secret'], + providesHistory: 'date', + providesFullHistory: true, + tid: 'tid', + tradable: true, + gekkoBroker: 0.6, + limitedCancelConfirmation: true + }; +}; + +module.exports = Trader; diff --git a/exchange/wrappers/bitcoin-co-id.js.old b/exchange/wrappers/bitcoin-co-id.js.old new file mode 100644 index 0000000..08c366d --- /dev/null +++ b/exchange/wrappers/bitcoin-co-id.js.old @@ -0,0 +1,296 @@ +var Bitcoincoid = require('bitcoin-co-id-update'), + _ = require('lodash'), + moment = require('moment'), + log = require('../core/log'); + +var Trader = function(config) { + _.bindAll(this); + if(_.isObject(config)) { + this.key = config.key; + this.secret = config.secret; + this.clientID = config.username; + this.currency = config.currency.toLowerCase(); + this.asset = config.asset.toLowerCase(); + this.pair = this.asset + '_' + this.currency; + } + this.name = 'Bitcoin-co-id'; + this.bitcoincoid = new Bitcoincoid(this.key, this.secret); +} + +Trader.prototype.roundAmount = function(amount) { + var priceDivider = 100000000; // one hundred million; + amount *= priceDivider; + amount = Math.floor(amount); + amount /= priceDivider; + return amount; +}; + +// if the exchange errors we try the same call again after +// waiting 10 seconds +Trader.prototype.retry = function(method, args) { + var wait = +moment.duration(10, 'seconds'); + log.debug(this.name, 'returned an error, retrying..'); + + var self = this; + + // make sure the callback (and any other fn) + // is bound to Trader + _.each(args, function(arg, i) { + if(_.isFunction(arg)) + args[i] = _.bind(arg, self); + }); + + // run the failed method again with the same + // arguments after wait + setTimeout( + function() { method.apply(self, args); }, + wait + ); +} + +Trader.prototype.getTicker = function(callback) { + var args = _.toArray(arguments), + set = function(err, data) { + if(err) + return this.retry(this.getTicker, args); + var ticker = { + ask: this.roundAmount(data.ticker.buy), + bid: this.roundAmount(data.ticker.sell), + }; + callback(err, ticker); + }.bind(this); + this.bitcoincoid.getTicker(this.pair, set); +} + +Trader.prototype.getPortfolio = function(callback) { + var functionName = 'Trader.getPortfolio()', + args = _.toArray(arguments); + set = function(err, data) { + if(err) + return this.retry(this.getPortfolio, args); + var assetAmount = this.roundAmount( data.return.balance[this.asset] ), + currencyAmount = this.roundAmount( data.return.balance[this.currency] ), + assetHold = this.roundAmount( data.return.balance_hold[this.asset] ), + currencyHold = this.roundAmount( data.return.balance_hold[this.currency] ); + + if( + !_.isNumber(assetAmount) || _.isNaN(assetAmount) || + !_.isNumber(currencyAmount) || _.isNaN(currencyAmount) + ) { + return log.error('account balance error: Gekko is unable to trade with ',this.currency.toUpperCase(),':',currencyAmount,' or ',this.asset.toUpperCase(),':',assetAmount); + } + var portfolio = [ + { name: this.currency.toUpperCase(), amount: currencyAmount - currencyHold}, + { name: this.asset.toUpperCase(), amount: assetAmount - assetHold } + ]; + callback(err, portfolio); + }.bind(this); + this.bitcoincoid.getAccountBalances(set); +} + +Trader.prototype.getTrades = function(since, callback, descending) { + var args = _.toArray(arguments); + + if(since) + since = 150; // ??? + + var process = function(err, data) { + if(err) + return this.retry(this.getTrades, args); + + var trades = _.map(data, function(trade) { + return { + price: +trade.price, + amount: +trade.amount, + tid: +trade.tid, + date: trade.date + }; + }); + callback(null, data.reverse()); + }.bind(this); + + this.bitcoincoid.getTrades(this.pair, process); +} + +// bitcoin.co.id: Maker 0% - Taker 0.3% +// Note: trading fee: 0.3% for IDR pairs and 0% for BTC pairs +Trader.prototype.getFee = function(callback) { + var fee = 0; + if (this.currency.toUpperCase() === 'IDR') + { + fee = 0.003; + } + else if (this.currency.toUpperCase() === 'BTC') + { + fee = 0.000; + } + + callback(false, fee); +}; + +Trader.prototype.buy = function(amount, price, callback) { + this.type = 'buy'; + + // decrease purchase amount by 1% to avoid trying to buy more than balance + amount -= amount / 100; + amount = this.roundAmount(amount); + + // decrease purchase price by 1% less than asking price + // price -= price / 100; + amount *= price; + + var set = function(err, data) { + if(!err && _.isEmpty(data)) + err = 'no data'; + else if(!err && !_.isEmpty(data.errorMessage)) + err = data.errorMessage; + if(err) + return log.error('unable to buy', err); + callback(null, data.return.order_id); + }.bind(this); + this.bitcoincoid.createOrders( + this.pair + '', + this.type, + price, + amount, + set + ); +} + +Trader.prototype.sell = function(amount, price, callback) { + this.type = 'sell'; + + // increase selling price by 1% more than bidding price + // price += price / 100; + + amount = this.roundAmount(amount); + var set = function(err, data) { + if(!err && _.isEmpty(data)) + err = 'no data'; + else if(!err && !_.isEmpty(data.errorMessage)) + err = data.errorMessage; + if(err) + return log.error('unable to sell', err); + callback(null, data.return.order_id); + }.bind(this); + this.bitcoincoid.createOrders( + this.pair + '', + this.type, + price, + amount, + set + ); +} + +Trader.prototype.checkOrder = function(order, callback) { + var args = _.toArray(arguments); + if (order === null) { + return callback('no order_id', false); + } + var check = function(err, data) { + if(err){ + return this.retry(this.checkOrder, arguments); + } + var status = data.return.order.status; + if (status === 'filled') { + return callback(err, true); + } else if (status === 'open') { + return callback(err, false); + } + callback(err, false); + }.bind(this); + + this.bitcoincoid.getOrderDetails(this.pair, order, check); +} + +Trader.prototype.getOrder = function(order, callback) { + var args = _.toArray(arguments); + if (order === null) { + return callback('no order_id', false); + } + var get = function(data, err) { + if(err) + return callback(err); + + var price = 0; + var amount = 0; + var date = moment(0); + + if(!data.success) + return callback(null, {price, amount, date}); + + var result = data.return.order; + var orderAmount = result.order+'_'+this.asset; + price = result.Price; + amount = result.orderAmount; + + if(result.status === 'open') { + date = moment(result.submit_time); + } else { + date = moment(result.finish_time); + } + callback(err, {price, amount, date}); + }.bind(this); + + this.bitcoincoid.getOrderDetails(this.pair, order, get); +} + +Trader.prototype.cancelOrder = function(order, callback) { + var args = _.toArray(arguments); + var cancel = function(err, data) { + if(err) { + return log.error('unable to cancel order: ', order, '(', err, '), retrying...'); + this.retry(this.cancelOrder, args); + } + callback(); + }; + this.bitcoincoid.cancelOrder(this.pair, order, this.type, cancel); +} + +Trader.getCapabilities = function () { + return { + name: 'Bitcoin.co.id', + slug: 'bitcoin-co-id', + currencies: ['IDR', 'BTC'], + assets: [ + "BTC", "BCH", "BTG", "ETH", "ETC", "LTC", "NXT", "WAVES", "STR", "XRP", "XZC", "BTS", "DRK", "DOGE", "NEM", "XZR", "DASH", "XLM", "XEM" + ], + markets: [ + + // IDR <-> XXXX + + { pair: ['IDR', 'BTC'], minimalOrder: { amount: 0.0001, unit: 'asset' } }, + { pair: ['IDR', 'BCH'], minimalOrder: { amount: 0.001, unit: 'asset' } }, + { pair: ['IDR', 'BTG'], minimalOrder: { amount: 0.01, unit: 'asset' } }, + { pair: ['IDR', 'ETH'], minimalOrder: { amount: 0.01, unit: 'asset' } }, + { pair: ['IDR', 'ETC'], minimalOrder: { amount: 0.1, unit: 'asset' } }, + { pair: ['IDR', 'LTC'], minimalOrder: { amount: 0.01, unit: 'asset' } }, + { pair: ['IDR', 'NXT'], minimalOrder: { amount: 5, unit: 'asset' } }, + { pair: ['IDR', 'WAVES'], minimalOrder: { amount: 0.1, unit: 'asset' } }, + { pair: ['IDR', 'STR'], minimalOrder: { amount: 20, unit: 'asset' } }, // Listed as XLM + { pair: ['IDR', 'XRP'], minimalOrder: { amount: 10, unit: 'asset' } }, + { pair: ['IDR', 'XZC'], minimalOrder: { amount: 0.1, unit: 'asset' } }, + + + // BTC <-> XXXX + + { pair: ['BTC', 'BTS'], minimalOrder: { amount: 0.01, unit: 'asset' } }, + { pair: ['BTC', 'DRK'], minimalOrder: { amount: 0.01, unit: 'asset' } }, // Listed as DASH + { pair: ['BTC', 'DOGE'], minimalOrder: { amount: 1, unit: 'asset' } }, + { pair: ['BTC', 'ETH'], minimalOrder: { amount: 0.001, unit: 'asset' } }, + { pair: ['BTC', 'LTC'], minimalOrder: { amount: 0.01, unit: 'asset' } }, + { pair: ['BTC', 'NXT'], minimalOrder: { amount: 0.01, unit: 'asset' } }, + { pair: ['BTC', 'STR'], minimalOrder: { amount: 0.01, unit: 'asset' } }, // Listed as XLM + { pair: ['BTC', 'NEM'], minimalOrder: { amount: 1, unit: 'asset' } }, // Listed as XEM + { pair: ['BTC', 'XRP'], minimalOrder: { amount: 0.01, unit: 'asset' } } + + ], + requires: ['key', 'secret'], + tid: 'tid', + providesHistory: true, + providesFullHistory: false, + tradable: true + }; +} + +module.exports = Trader; diff --git a/exchange/wrappers/bitfinex-markets.json b/exchange/wrappers/bitfinex-markets.json new file mode 100644 index 0000000..ae863e5 --- /dev/null +++ b/exchange/wrappers/bitfinex-markets.json @@ -0,0 +1,3264 @@ +{ + "assets": [ + "BTC", + "LTC", + "ETH", + "ETC", + "RRT", + "ZEC", + "XMR", + "DSH", + "XRP", + "IOT", + "EOS", + "SAN", + "OMG", + "NEO", + "ETP", + "QTM", + "AVT", + "EDO", + "BTG", + "DAT", + "QSH", + "YYW", + "GNT", + "SNT", + "BAT", + "MNA", + "FUN", + "ZRX", + "TNB", + "SPK", + "TRX", + "RCN", + "RLC", + "AID", + "SNG", + "REP", + "ELF", + "IOS", + "AIO", + "REQ", + "RDN", + "LRC", + "WAX", + "DAI", + "CFI", + "AGI", + "BFT", + "MTN", + "ODE", + "ANT", + "DTH", + "MIT", + "STJ", + "XLM", + "XVG", + "BCI", + "MKR", + "KNC", + "POA", + "LYM", + "UTK", + "VEE", + "DAD", + "ORS", + "AUC", + "POY", + "FSN", + "CBT", + "ZCN", + "SEN", + "NCA", + "CND", + "CTX", + "PAI", + "SEE", + "ESS", + "ATM", + "HOT", + "DTA", + "IQX", + "WPR", + "ZIL", + "BNT", + "ABS", + "XRA", + "MAN", + "BBN", + "NIO", + "DGX", + "VET", + "UTN", + "TKN", + "GOT", + "XTZ", + "CNN", + "BOX", + "MGO", + "RTE", + "YGG", + "MLN", + "WTC", + "CSX", + "OMN", + "INT", + "DRN", + "PNK", + "DGB", + "BSV", + "BAB" + ], + "currencies": [ + "USD", + "BTC", + "EUR", + "JPY", + "ETH", + "GBP", + "EOS" + ], + "markets": [ + { + "pair": [ + "USD", + "BTC" + ], + "minimalOrder": { + "amount": "0.002", + "unit": "asset" + } + }, + { + "pair": [ + "USD", + "LTC" + ], + "minimalOrder": { + "amount": "0.2", + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "LTC" + ], + "minimalOrder": { + "amount": "0.2", + "unit": "asset" + } + }, + { + "pair": [ + "USD", + "ETH" + ], + "minimalOrder": { + "amount": "0.06", + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "ETH" + ], + "minimalOrder": { + "amount": "0.06", + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "ETC" + ], + "minimalOrder": { + "amount": "2.0", + "unit": "asset" + } + }, + { + "pair": [ + "USD", + "ETC" + ], + "minimalOrder": { + "amount": "2.0", + "unit": "asset" + } + }, + { + "pair": [ + "USD", + "RRT" + ], + "minimalOrder": { + "amount": "294.0", + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "RRT" + ], + "minimalOrder": { + "amount": "294.0", + "unit": "asset" + } + }, + { + "pair": [ + "USD", + "ZEC" + ], + "minimalOrder": { + "amount": "0.1", + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "ZEC" + ], + "minimalOrder": { + "amount": "0.1", + "unit": "asset" + } + }, + { + "pair": [ + "USD", + "XMR" + ], + "minimalOrder": { + "amount": "0.2", + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "XMR" + ], + "minimalOrder": { + "amount": "0.2", + "unit": "asset" + } + }, + { + "pair": [ + "USD", + "DSH" + ], + "minimalOrder": { + "amount": "0.08", + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "DSH" + ], + "minimalOrder": { + "amount": "0.08", + "unit": "asset" + } + }, + { + "pair": [ + "EUR", + "BTC" + ], + "minimalOrder": { + "amount": "0.002", + "unit": "asset" + } + }, + { + "pair": [ + "JPY", + "BTC" + ], + "minimalOrder": { + "amount": "0.002", + "unit": "asset" + } + }, + { + "pair": [ + "USD", + "XRP" + ], + "minimalOrder": { + "amount": "38.0", + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "XRP" + ], + "minimalOrder": { + "amount": "38.0", + "unit": "asset" + } + }, + { + "pair": [ + "USD", + "IOT" + ], + "minimalOrder": { + "amount": "24.0", + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "IOT" + ], + "minimalOrder": { + "amount": "24.0", + "unit": "asset" + } + }, + { + "pair": [ + "ETH", + "IOT" + ], + "minimalOrder": { + "amount": "24.0", + "unit": "asset" + } + }, + { + "pair": [ + "USD", + "EOS" + ], + "minimalOrder": { + "amount": "2.0", + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "EOS" + ], + "minimalOrder": { + "amount": "2.0", + "unit": "asset" + } + }, + { + "pair": [ + "ETH", + "EOS" + ], + "minimalOrder": { + "amount": "2.0", + "unit": "asset" + } + }, + { + "pair": [ + "USD", + "SAN" + ], + "minimalOrder": { + "amount": "24.0", + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "SAN" + ], + "minimalOrder": { + "amount": "24.0", + "unit": "asset" + } + }, + { + "pair": [ + "ETH", + "SAN" + ], + "minimalOrder": { + "amount": "24.0", + "unit": "asset" + } + }, + { + "pair": [ + "USD", + "OMG" + ], + "minimalOrder": { + "amount": "4.0", + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "OMG" + ], + "minimalOrder": { + "amount": "4.0", + "unit": "asset" + } + }, + { + "pair": [ + "ETH", + "OMG" + ], + "minimalOrder": { + "amount": "4.0", + "unit": "asset" + } + }, + { + "pair": [ + "USD", + "NEO" + ], + "minimalOrder": { + "amount": "0.8", + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "NEO" + ], + "minimalOrder": { + "amount": "0.8", + "unit": "asset" + } + }, + { + "pair": [ + "ETH", + "NEO" + ], + "minimalOrder": { + "amount": "0.8", + "unit": "asset" + } + }, + { + "pair": [ + "USD", + "ETP" + ], + "minimalOrder": { + "amount": "8.0", + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "ETP" + ], + "minimalOrder": { + "amount": "8.0", + "unit": "asset" + } + }, + { + "pair": [ + "ETH", + "ETP" + ], + "minimalOrder": { + "amount": "8.0", + "unit": "asset" + } + }, + { + "pair": [ + "USD", + "QTM" + ], + "minimalOrder": { + "amount": "4.0", + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "QTM" + ], + "minimalOrder": { + "amount": "4.0", + "unit": "asset" + } + }, + { + "pair": [ + "ETH", + "QTM" + ], + "minimalOrder": { + "amount": "4.0", + "unit": "asset" + } + }, + { + "pair": [ + "USD", + "AVT" + ], + "minimalOrder": { + "amount": "36.0", + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "AVT" + ], + "minimalOrder": { + "amount": "36.0", + "unit": "asset" + } + }, + { + "pair": [ + "ETH", + "AVT" + ], + "minimalOrder": { + "amount": "36.0", + "unit": "asset" + } + }, + { + "pair": [ + "USD", + "EDO" + ], + "minimalOrder": { + "amount": "16.0", + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "EDO" + ], + "minimalOrder": { + "amount": "16.0", + "unit": "asset" + } + }, + { + "pair": [ + "ETH", + "EDO" + ], + "minimalOrder": { + "amount": "16.0", + "unit": "asset" + } + }, + { + "pair": [ + "USD", + "BTG" + ], + "minimalOrder": { + "amount": "0.6", + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "BTG" + ], + "minimalOrder": { + "amount": "0.6", + "unit": "asset" + } + }, + { + "pair": [ + "USD", + "DAT" + ], + "minimalOrder": { + "amount": "352.0", + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "DAT" + ], + "minimalOrder": { + "amount": "352.0", + "unit": "asset" + } + }, + { + "pair": [ + "ETH", + "DAT" + ], + "minimalOrder": { + "amount": "352.0", + "unit": "asset" + } + }, + { + "pair": [ + "USD", + "QSH" + ], + "minimalOrder": { + "amount": "68.0", + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "QSH" + ], + "minimalOrder": { + "amount": "68.0", + "unit": "asset" + } + }, + { + "pair": [ + "ETH", + "QSH" + ], + "minimalOrder": { + "amount": "68.0", + "unit": "asset" + } + }, + { + "pair": [ + "USD", + "YYW" + ], + "minimalOrder": { + "amount": "510.0", + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "YYW" + ], + "minimalOrder": { + "amount": "510.0", + "unit": "asset" + } + }, + { + "pair": [ + "ETH", + "YYW" + ], + "minimalOrder": { + "amount": "510.0", + "unit": "asset" + } + }, + { + "pair": [ + "USD", + "GNT" + ], + "minimalOrder": { + "amount": "82.0", + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "GNT" + ], + "minimalOrder": { + "amount": "82.0", + "unit": "asset" + } + }, + { + "pair": [ + "ETH", + "GNT" + ], + "minimalOrder": { + "amount": "82.0", + "unit": "asset" + } + }, + { + "pair": [ + "USD", + "SNT" + ], + "minimalOrder": { + "amount": "334.0", + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "SNT" + ], + "minimalOrder": { + "amount": "334.0", + "unit": "asset" + } + }, + { + "pair": [ + "ETH", + "SNT" + ], + "minimalOrder": { + "amount": "334.0", + "unit": "asset" + } + }, + { + "pair": [ + "EUR", + "IOT" + ], + "minimalOrder": { + "amount": "24.0", + "unit": "asset" + } + }, + { + "pair": [ + "USD", + "BAT" + ], + "minimalOrder": { + "amount": "70.0", + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "BAT" + ], + "minimalOrder": { + "amount": "70.0", + "unit": "asset" + } + }, + { + "pair": [ + "ETH", + "BAT" + ], + "minimalOrder": { + "amount": "70.0", + "unit": "asset" + } + }, + { + "pair": [ + "USD", + "MNA" + ], + "minimalOrder": { + "amount": "164.0", + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "MNA" + ], + "minimalOrder": { + "amount": "164.0", + "unit": "asset" + } + }, + { + "pair": [ + "ETH", + "MNA" + ], + "minimalOrder": { + "amount": "164.0", + "unit": "asset" + } + }, + { + "pair": [ + "USD", + "FUN" + ], + "minimalOrder": { + "amount": "1196.0", + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "FUN" + ], + "minimalOrder": { + "amount": "1196.0", + "unit": "asset" + } + }, + { + "pair": [ + "ETH", + "FUN" + ], + "minimalOrder": { + "amount": "1196.0", + "unit": "asset" + } + }, + { + "pair": [ + "USD", + "ZRX" + ], + "minimalOrder": { + "amount": "20.0", + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "ZRX" + ], + "minimalOrder": { + "amount": "20.0", + "unit": "asset" + } + }, + { + "pair": [ + "ETH", + "ZRX" + ], + "minimalOrder": { + "amount": "20.0", + "unit": "asset" + } + }, + { + "pair": [ + "USD", + "TNB" + ], + "minimalOrder": { + "amount": "1418.0", + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "TNB" + ], + "minimalOrder": { + "amount": "1418.0", + "unit": "asset" + } + }, + { + "pair": [ + "ETH", + "TNB" + ], + "minimalOrder": { + "amount": "1418.0", + "unit": "asset" + } + }, + { + "pair": [ + "USD", + "SPK" + ], + "minimalOrder": { + "amount": "320.0", + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "SPK" + ], + "minimalOrder": { + "amount": "320.0", + "unit": "asset" + } + }, + { + "pair": [ + "ETH", + "SPK" + ], + "minimalOrder": { + "amount": "320.0", + "unit": "asset" + } + }, + { + "pair": [ + "USD", + "TRX" + ], + "minimalOrder": { + "amount": "548.0", + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "TRX" + ], + "minimalOrder": { + "amount": "548.0", + "unit": "asset" + } + }, + { + "pair": [ + "ETH", + "TRX" + ], + "minimalOrder": { + "amount": "548.0", + "unit": "asset" + } + }, + { + "pair": [ + "USD", + "RCN" + ], + "minimalOrder": { + "amount": "518.0", + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "RCN" + ], + "minimalOrder": { + "amount": "518.0", + "unit": "asset" + } + }, + { + "pair": [ + "ETH", + "RCN" + ], + "minimalOrder": { + "amount": "518.0", + "unit": "asset" + } + }, + { + "pair": [ + "USD", + "RLC" + ], + "minimalOrder": { + "amount": "32.0", + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "RLC" + ], + "minimalOrder": { + "amount": "32.0", + "unit": "asset" + } + }, + { + "pair": [ + "ETH", + "RLC" + ], + "minimalOrder": { + "amount": "32.0", + "unit": "asset" + } + }, + { + "pair": [ + "USD", + "AID" + ], + "minimalOrder": { + "amount": "236.0", + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "AID" + ], + "minimalOrder": { + "amount": "236.0", + "unit": "asset" + } + }, + { + "pair": [ + "ETH", + "AID" + ], + "minimalOrder": { + "amount": "236.0", + "unit": "asset" + } + }, + { + "pair": [ + "USD", + "SNG" + ], + "minimalOrder": { + "amount": "562.0", + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "SNG" + ], + "minimalOrder": { + "amount": "562.0", + "unit": "asset" + } + }, + { + "pair": [ + "ETH", + "SNG" + ], + "minimalOrder": { + "amount": "562.0", + "unit": "asset" + } + }, + { + "pair": [ + "USD", + "REP" + ], + "minimalOrder": { + "amount": "1.0", + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "REP" + ], + "minimalOrder": { + "amount": "1.0", + "unit": "asset" + } + }, + { + "pair": [ + "ETH", + "REP" + ], + "minimalOrder": { + "amount": "1.0", + "unit": "asset" + } + }, + { + "pair": [ + "USD", + "ELF" + ], + "minimalOrder": { + "amount": "46.0", + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "ELF" + ], + "minimalOrder": { + "amount": "46.0", + "unit": "asset" + } + }, + { + "pair": [ + "ETH", + "ELF" + ], + "minimalOrder": { + "amount": "46.0", + "unit": "asset" + } + }, + { + "pair": [ + "GBP", + "BTC" + ], + "minimalOrder": { + "amount": "0.002", + "unit": "asset" + } + }, + { + "pair": [ + "EUR", + "ETH" + ], + "minimalOrder": { + "amount": "0.06", + "unit": "asset" + } + }, + { + "pair": [ + "JPY", + "ETH" + ], + "minimalOrder": { + "amount": "0.06", + "unit": "asset" + } + }, + { + "pair": [ + "GBP", + "ETH" + ], + "minimalOrder": { + "amount": "0.06", + "unit": "asset" + } + }, + { + "pair": [ + "EUR", + "NEO" + ], + "minimalOrder": { + "amount": "0.8", + "unit": "asset" + } + }, + { + "pair": [ + "JPY", + "NEO" + ], + "minimalOrder": { + "amount": "0.8", + "unit": "asset" + } + }, + { + "pair": [ + "GBP", + "NEO" + ], + "minimalOrder": { + "amount": "0.8", + "unit": "asset" + } + }, + { + "pair": [ + "EUR", + "EOS" + ], + "minimalOrder": { + "amount": "2.0", + "unit": "asset" + } + }, + { + "pair": [ + "JPY", + "EOS" + ], + "minimalOrder": { + "amount": "2.0", + "unit": "asset" + } + }, + { + "pair": [ + "GBP", + "EOS" + ], + "minimalOrder": { + "amount": "2.0", + "unit": "asset" + } + }, + { + "pair": [ + "JPY", + "IOT" + ], + "minimalOrder": { + "amount": "24.0", + "unit": "asset" + } + }, + { + "pair": [ + "GBP", + "IOT" + ], + "minimalOrder": { + "amount": "24.0", + "unit": "asset" + } + }, + { + "pair": [ + "USD", + "IOS" + ], + "minimalOrder": { + "amount": "1170.0", + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "IOS" + ], + "minimalOrder": { + "amount": "1170.0", + "unit": "asset" + } + }, + { + "pair": [ + "ETH", + "IOS" + ], + "minimalOrder": { + "amount": "1170.0", + "unit": "asset" + } + }, + { + "pair": [ + "USD", + "AIO" + ], + "minimalOrder": { + "amount": "34.0", + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "AIO" + ], + "minimalOrder": { + "amount": "34.0", + "unit": "asset" + } + }, + { + "pair": [ + "ETH", + "AIO" + ], + "minimalOrder": { + "amount": "34.0", + "unit": "asset" + } + }, + { + "pair": [ + "USD", + "REQ" + ], + "minimalOrder": { + "amount": "302.0", + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "REQ" + ], + "minimalOrder": { + "amount": "302.0", + "unit": "asset" + } + }, + { + "pair": [ + "ETH", + "REQ" + ], + "minimalOrder": { + "amount": "302.0", + "unit": "asset" + } + }, + { + "pair": [ + "USD", + "RDN" + ], + "minimalOrder": { + "amount": "32.0", + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "RDN" + ], + "minimalOrder": { + "amount": "32.0", + "unit": "asset" + } + }, + { + "pair": [ + "ETH", + "RDN" + ], + "minimalOrder": { + "amount": "32.0", + "unit": "asset" + } + }, + { + "pair": [ + "USD", + "LRC" + ], + "minimalOrder": { + "amount": "144.0", + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "LRC" + ], + "minimalOrder": { + "amount": "144.0", + "unit": "asset" + } + }, + { + "pair": [ + "ETH", + "LRC" + ], + "minimalOrder": { + "amount": "144.0", + "unit": "asset" + } + }, + { + "pair": [ + "USD", + "WAX" + ], + "minimalOrder": { + "amount": "166.0", + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "WAX" + ], + "minimalOrder": { + "amount": "166.0", + "unit": "asset" + } + }, + { + "pair": [ + "ETH", + "WAX" + ], + "minimalOrder": { + "amount": "166.0", + "unit": "asset" + } + }, + { + "pair": [ + "USD", + "DAI" + ], + "minimalOrder": { + "amount": "10.0", + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "DAI" + ], + "minimalOrder": { + "amount": "10.0", + "unit": "asset" + } + }, + { + "pair": [ + "ETH", + "DAI" + ], + "minimalOrder": { + "amount": "10.0", + "unit": "asset" + } + }, + { + "pair": [ + "USD", + "CFI" + ], + "minimalOrder": { + "amount": "566.0", + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "CFI" + ], + "minimalOrder": { + "amount": "566.0", + "unit": "asset" + } + }, + { + "pair": [ + "ETH", + "CFI" + ], + "minimalOrder": { + "amount": "566.0", + "unit": "asset" + } + }, + { + "pair": [ + "USD", + "AGI" + ], + "minimalOrder": { + "amount": "268.0", + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "AGI" + ], + "minimalOrder": { + "amount": "268.0", + "unit": "asset" + } + }, + { + "pair": [ + "ETH", + "AGI" + ], + "minimalOrder": { + "amount": "268.0", + "unit": "asset" + } + }, + { + "pair": [ + "USD", + "BFT" + ], + "minimalOrder": { + "amount": "272.0", + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "BFT" + ], + "minimalOrder": { + "amount": "272.0", + "unit": "asset" + } + }, + { + "pair": [ + "ETH", + "BFT" + ], + "minimalOrder": { + "amount": "272.0", + "unit": "asset" + } + }, + { + "pair": [ + "USD", + "MTN" + ], + "minimalOrder": { + "amount": "606.0", + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "MTN" + ], + "minimalOrder": { + "amount": "606.0", + "unit": "asset" + } + }, + { + "pair": [ + "ETH", + "MTN" + ], + "minimalOrder": { + "amount": "606.0", + "unit": "asset" + } + }, + { + "pair": [ + "USD", + "ODE" + ], + "minimalOrder": { + "amount": "86.0", + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "ODE" + ], + "minimalOrder": { + "amount": "86.0", + "unit": "asset" + } + }, + { + "pair": [ + "ETH", + "ODE" + ], + "minimalOrder": { + "amount": "86.0", + "unit": "asset" + } + }, + { + "pair": [ + "USD", + "ANT" + ], + "minimalOrder": { + "amount": "22.0", + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "ANT" + ], + "minimalOrder": { + "amount": "22.0", + "unit": "asset" + } + }, + { + "pair": [ + "ETH", + "ANT" + ], + "minimalOrder": { + "amount": "22.0", + "unit": "asset" + } + }, + { + "pair": [ + "USD", + "DTH" + ], + "minimalOrder": { + "amount": "958.0", + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "DTH" + ], + "minimalOrder": { + "amount": "958.0", + "unit": "asset" + } + }, + { + "pair": [ + "ETH", + "DTH" + ], + "minimalOrder": { + "amount": "958.0", + "unit": "asset" + } + }, + { + "pair": [ + "USD", + "MIT" + ], + "minimalOrder": { + "amount": "44.0", + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "MIT" + ], + "minimalOrder": { + "amount": "44.0", + "unit": "asset" + } + }, + { + "pair": [ + "ETH", + "MIT" + ], + "minimalOrder": { + "amount": "44.0", + "unit": "asset" + } + }, + { + "pair": [ + "USD", + "STJ" + ], + "minimalOrder": { + "amount": "44.0", + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "STJ" + ], + "minimalOrder": { + "amount": "44.0", + "unit": "asset" + } + }, + { + "pair": [ + "ETH", + "STJ" + ], + "minimalOrder": { + "amount": "44.0", + "unit": "asset" + } + }, + { + "pair": [ + "USD", + "XLM" + ], + "minimalOrder": { + "amount": "56.0", + "unit": "asset" + } + }, + { + "pair": [ + "EUR", + "XLM" + ], + "minimalOrder": { + "amount": "56.0", + "unit": "asset" + } + }, + { + "pair": [ + "JPY", + "XLM" + ], + "minimalOrder": { + "amount": "56.0", + "unit": "asset" + } + }, + { + "pair": [ + "GBP", + "XLM" + ], + "minimalOrder": { + "amount": "56.0", + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "XLM" + ], + "minimalOrder": { + "amount": "56.0", + "unit": "asset" + } + }, + { + "pair": [ + "ETH", + "XLM" + ], + "minimalOrder": { + "amount": "56.0", + "unit": "asset" + } + }, + { + "pair": [ + "USD", + "XVG" + ], + "minimalOrder": { + "amount": "1024.0", + "unit": "asset" + } + }, + { + "pair": [ + "EUR", + "XVG" + ], + "minimalOrder": { + "amount": "1024.0", + "unit": "asset" + } + }, + { + "pair": [ + "JPY", + "XVG" + ], + "minimalOrder": { + "amount": "1024.0", + "unit": "asset" + } + }, + { + "pair": [ + "GBP", + "XVG" + ], + "minimalOrder": { + "amount": "1024.0", + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "XVG" + ], + "minimalOrder": { + "amount": "1024.0", + "unit": "asset" + } + }, + { + "pair": [ + "ETH", + "XVG" + ], + "minimalOrder": { + "amount": "1024.0", + "unit": "asset" + } + }, + { + "pair": [ + "USD", + "BCI" + ], + "minimalOrder": { + "amount": "16.0", + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "BCI" + ], + "minimalOrder": { + "amount": "16.0", + "unit": "asset" + } + }, + { + "pair": [ + "USD", + "MKR" + ], + "minimalOrder": { + "amount": "0.04", + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "MKR" + ], + "minimalOrder": { + "amount": "0.04", + "unit": "asset" + } + }, + { + "pair": [ + "ETH", + "MKR" + ], + "minimalOrder": { + "amount": "0.04", + "unit": "asset" + } + }, + { + "pair": [ + "USD", + "KNC" + ], + "minimalOrder": { + "amount": "36.0", + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "KNC" + ], + "minimalOrder": { + "amount": "36.0", + "unit": "asset" + } + }, + { + "pair": [ + "ETH", + "KNC" + ], + "minimalOrder": { + "amount": "36.0", + "unit": "asset" + } + }, + { + "pair": [ + "USD", + "POA" + ], + "minimalOrder": { + "amount": "184.0", + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "POA" + ], + "minimalOrder": { + "amount": "184.0", + "unit": "asset" + } + }, + { + "pair": [ + "ETH", + "POA" + ], + "minimalOrder": { + "amount": "184.0", + "unit": "asset" + } + }, + { + "pair": [ + "USD", + "LYM" + ], + "minimalOrder": { + "amount": "874.0", + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "LYM" + ], + "minimalOrder": { + "amount": "874.0", + "unit": "asset" + } + }, + { + "pair": [ + "ETH", + "LYM" + ], + "minimalOrder": { + "amount": "874.0", + "unit": "asset" + } + }, + { + "pair": [ + "USD", + "UTK" + ], + "minimalOrder": { + "amount": "426.0", + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "UTK" + ], + "minimalOrder": { + "amount": "426.0", + "unit": "asset" + } + }, + { + "pair": [ + "ETH", + "UTK" + ], + "minimalOrder": { + "amount": "426.0", + "unit": "asset" + } + }, + { + "pair": [ + "USD", + "VEE" + ], + "minimalOrder": { + "amount": "1430.0", + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "VEE" + ], + "minimalOrder": { + "amount": "1430.0", + "unit": "asset" + } + }, + { + "pair": [ + "ETH", + "VEE" + ], + "minimalOrder": { + "amount": "1430.0", + "unit": "asset" + } + }, + { + "pair": [ + "USD", + "DAD" + ], + "minimalOrder": { + "amount": "140.0", + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "DAD" + ], + "minimalOrder": { + "amount": "140.0", + "unit": "asset" + } + }, + { + "pair": [ + "ETH", + "DAD" + ], + "minimalOrder": { + "amount": "140.0", + "unit": "asset" + } + }, + { + "pair": [ + "USD", + "ORS" + ], + "minimalOrder": { + "amount": "476.0", + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "ORS" + ], + "minimalOrder": { + "amount": "476.0", + "unit": "asset" + } + }, + { + "pair": [ + "ETH", + "ORS" + ], + "minimalOrder": { + "amount": "476.0", + "unit": "asset" + } + }, + { + "pair": [ + "USD", + "AUC" + ], + "minimalOrder": { + "amount": "304.0", + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "AUC" + ], + "minimalOrder": { + "amount": "304.0", + "unit": "asset" + } + }, + { + "pair": [ + "ETH", + "AUC" + ], + "minimalOrder": { + "amount": "304.0", + "unit": "asset" + } + }, + { + "pair": [ + "USD", + "POY" + ], + "minimalOrder": { + "amount": "70.0", + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "POY" + ], + "minimalOrder": { + "amount": "70.0", + "unit": "asset" + } + }, + { + "pair": [ + "ETH", + "POY" + ], + "minimalOrder": { + "amount": "70.0", + "unit": "asset" + } + }, + { + "pair": [ + "USD", + "FSN" + ], + "minimalOrder": { + "amount": "14.0", + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "FSN" + ], + "minimalOrder": { + "amount": "14.0", + "unit": "asset" + } + }, + { + "pair": [ + "ETH", + "FSN" + ], + "minimalOrder": { + "amount": "14.0", + "unit": "asset" + } + }, + { + "pair": [ + "USD", + "CBT" + ], + "minimalOrder": { + "amount": "1228.0", + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "CBT" + ], + "minimalOrder": { + "amount": "1228.0", + "unit": "asset" + } + }, + { + "pair": [ + "ETH", + "CBT" + ], + "minimalOrder": { + "amount": "1228.0", + "unit": "asset" + } + }, + { + "pair": [ + "USD", + "ZCN" + ], + "minimalOrder": { + "amount": "90.0", + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "ZCN" + ], + "minimalOrder": { + "amount": "90.0", + "unit": "asset" + } + }, + { + "pair": [ + "ETH", + "ZCN" + ], + "minimalOrder": { + "amount": "90.0", + "unit": "asset" + } + }, + { + "pair": [ + "USD", + "SEN" + ], + "minimalOrder": { + "amount": "3840.0", + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "SEN" + ], + "minimalOrder": { + "amount": "3840.0", + "unit": "asset" + } + }, + { + "pair": [ + "ETH", + "SEN" + ], + "minimalOrder": { + "amount": "3840.0", + "unit": "asset" + } + }, + { + "pair": [ + "USD", + "NCA" + ], + "minimalOrder": { + "amount": "2572.0", + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "NCA" + ], + "minimalOrder": { + "amount": "2572.0", + "unit": "asset" + } + }, + { + "pair": [ + "ETH", + "NCA" + ], + "minimalOrder": { + "amount": "2572.0", + "unit": "asset" + } + }, + { + "pair": [ + "USD", + "CND" + ], + "minimalOrder": { + "amount": "606.0", + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "CND" + ], + "minimalOrder": { + "amount": "606.0", + "unit": "asset" + } + }, + { + "pair": [ + "ETH", + "CND" + ], + "minimalOrder": { + "amount": "606.0", + "unit": "asset" + } + }, + { + "pair": [ + "USD", + "CTX" + ], + "minimalOrder": { + "amount": "48.0", + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "CTX" + ], + "minimalOrder": { + "amount": "48.0", + "unit": "asset" + } + }, + { + "pair": [ + "ETH", + "CTX" + ], + "minimalOrder": { + "amount": "48.0", + "unit": "asset" + } + }, + { + "pair": [ + "USD", + "PAI" + ], + "minimalOrder": { + "amount": "82.0", + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "PAI" + ], + "minimalOrder": { + "amount": "82.0", + "unit": "asset" + } + }, + { + "pair": [ + "USD", + "SEE" + ], + "minimalOrder": { + "amount": "12382.0", + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "SEE" + ], + "minimalOrder": { + "amount": "12382.0", + "unit": "asset" + } + }, + { + "pair": [ + "ETH", + "SEE" + ], + "minimalOrder": { + "amount": "12382.0", + "unit": "asset" + } + }, + { + "pair": [ + "USD", + "ESS" + ], + "minimalOrder": { + "amount": "3054.0", + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "ESS" + ], + "minimalOrder": { + "amount": "3054.0", + "unit": "asset" + } + }, + { + "pair": [ + "ETH", + "ESS" + ], + "minimalOrder": { + "amount": "3054.0", + "unit": "asset" + } + }, + { + "pair": [ + "USD", + "ATM" + ], + "minimalOrder": { + "amount": "1842.0", + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "ATM" + ], + "minimalOrder": { + "amount": "1842.0", + "unit": "asset" + } + }, + { + "pair": [ + "ETH", + "ATM" + ], + "minimalOrder": { + "amount": "1842.0", + "unit": "asset" + } + }, + { + "pair": [ + "USD", + "HOT" + ], + "minimalOrder": { + "amount": "1408.0", + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "HOT" + ], + "minimalOrder": { + "amount": "1408.0", + "unit": "asset" + } + }, + { + "pair": [ + "ETH", + "HOT" + ], + "minimalOrder": { + "amount": "1408.0", + "unit": "asset" + } + }, + { + "pair": [ + "USD", + "DTA" + ], + "minimalOrder": { + "amount": "5572.0", + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "DTA" + ], + "minimalOrder": { + "amount": "5572.0", + "unit": "asset" + } + }, + { + "pair": [ + "ETH", + "DTA" + ], + "minimalOrder": { + "amount": "5572.0", + "unit": "asset" + } + }, + { + "pair": [ + "USD", + "IQX" + ], + "minimalOrder": { + "amount": "1798.0", + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "IQX" + ], + "minimalOrder": { + "amount": "1798.0", + "unit": "asset" + } + }, + { + "pair": [ + "EOS", + "IQX" + ], + "minimalOrder": { + "amount": "1798.0", + "unit": "asset" + } + }, + { + "pair": [ + "USD", + "WPR" + ], + "minimalOrder": { + "amount": "590.0", + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "WPR" + ], + "minimalOrder": { + "amount": "590.0", + "unit": "asset" + } + }, + { + "pair": [ + "ETH", + "WPR" + ], + "minimalOrder": { + "amount": "590.0", + "unit": "asset" + } + }, + { + "pair": [ + "USD", + "ZIL" + ], + "minimalOrder": { + "amount": "382.0", + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "ZIL" + ], + "minimalOrder": { + "amount": "382.0", + "unit": "asset" + } + }, + { + "pair": [ + "ETH", + "ZIL" + ], + "minimalOrder": { + "amount": "382.0", + "unit": "asset" + } + }, + { + "pair": [ + "USD", + "BNT" + ], + "minimalOrder": { + "amount": "10.0", + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "BNT" + ], + "minimalOrder": { + "amount": "10.0", + "unit": "asset" + } + }, + { + "pair": [ + "ETH", + "BNT" + ], + "minimalOrder": { + "amount": "10.0", + "unit": "asset" + } + }, + { + "pair": [ + "USD", + "ABS" + ], + "minimalOrder": { + "amount": "1136.0", + "unit": "asset" + } + }, + { + "pair": [ + "ETH", + "ABS" + ], + "minimalOrder": { + "amount": "1136.0", + "unit": "asset" + } + }, + { + "pair": [ + "USD", + "XRA" + ], + "minimalOrder": { + "amount": "296.0", + "unit": "asset" + } + }, + { + "pair": [ + "ETH", + "XRA" + ], + "minimalOrder": { + "amount": "296.0", + "unit": "asset" + } + }, + { + "pair": [ + "USD", + "MAN" + ], + "minimalOrder": { + "amount": "56.0", + "unit": "asset" + } + }, + { + "pair": [ + "ETH", + "MAN" + ], + "minimalOrder": { + "amount": "56.0", + "unit": "asset" + } + }, + { + "pair": [ + "USD", + "BBN" + ], + "minimalOrder": { + "amount": "1782.0", + "unit": "asset" + } + }, + { + "pair": [ + "ETH", + "BBN" + ], + "minimalOrder": { + "amount": "1782.0", + "unit": "asset" + } + }, + { + "pair": [ + "USD", + "NIO" + ], + "minimalOrder": { + "amount": "676.0", + "unit": "asset" + } + }, + { + "pair": [ + "ETH", + "NIO" + ], + "minimalOrder": { + "amount": "676.0", + "unit": "asset" + } + }, + { + "pair": [ + "USD", + "DGX" + ], + "minimalOrder": { + "amount": "0.2", + "unit": "asset" + } + }, + { + "pair": [ + "ETH", + "DGX" + ], + "minimalOrder": { + "amount": "0.2", + "unit": "asset" + } + }, + { + "pair": [ + "USD", + "VET" + ], + "minimalOrder": { + "amount": "1286.0", + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "VET" + ], + "minimalOrder": { + "amount": "1286.0", + "unit": "asset" + } + }, + { + "pair": [ + "ETH", + "VET" + ], + "minimalOrder": { + "amount": "1286.0", + "unit": "asset" + } + }, + { + "pair": [ + "USD", + "UTN" + ], + "minimalOrder": { + "amount": "2698.0", + "unit": "asset" + } + }, + { + "pair": [ + "ETH", + "UTN" + ], + "minimalOrder": { + "amount": "2698.0", + "unit": "asset" + } + }, + { + "pair": [ + "USD", + "TKN" + ], + "minimalOrder": { + "amount": "32.0", + "unit": "asset" + } + }, + { + "pair": [ + "ETH", + "TKN" + ], + "minimalOrder": { + "amount": "32.0", + "unit": "asset" + } + }, + { + "pair": [ + "USD", + "GOT" + ], + "minimalOrder": { + "amount": "20.0", + "unit": "asset" + } + }, + { + "pair": [ + "EUR", + "GOT" + ], + "minimalOrder": { + "amount": "20.0", + "unit": "asset" + } + }, + { + "pair": [ + "ETH", + "GOT" + ], + "minimalOrder": { + "amount": "20.0", + "unit": "asset" + } + }, + { + "pair": [ + "USD", + "XTZ" + ], + "minimalOrder": { + "amount": "8.0", + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "XTZ" + ], + "minimalOrder": { + "amount": "8.0", + "unit": "asset" + } + }, + { + "pair": [ + "USD", + "CNN" + ], + "minimalOrder": { + "amount": "34370.0", + "unit": "asset" + } + }, + { + "pair": [ + "ETH", + "CNN" + ], + "minimalOrder": { + "amount": "34370.0", + "unit": "asset" + } + }, + { + "pair": [ + "USD", + "BOX" + ], + "minimalOrder": { + "amount": "1252.0", + "unit": "asset" + } + }, + { + "pair": [ + "ETH", + "BOX" + ], + "minimalOrder": { + "amount": "1252.0", + "unit": "asset" + } + }, + { + "pair": [ + "EUR", + "TRX" + ], + "minimalOrder": { + "amount": "526.0", + "unit": "asset" + } + }, + { + "pair": [ + "GBP", + "TRX" + ], + "minimalOrder": { + "amount": "526.0", + "unit": "asset" + } + }, + { + "pair": [ + "JPY", + "TRX" + ], + "minimalOrder": { + "amount": "526.0", + "unit": "asset" + } + }, + { + "pair": [ + "USD", + "MGO" + ], + "minimalOrder": { + "amount": "30.0", + "unit": "asset" + } + }, + { + "pair": [ + "ETH", + "MGO" + ], + "minimalOrder": { + "amount": "30.0", + "unit": "asset" + } + }, + { + "pair": [ + "USD", + "RTE" + ], + "minimalOrder": { + "amount": "2950.0", + "unit": "asset" + } + }, + { + "pair": [ + "ETH", + "RTE" + ], + "minimalOrder": { + "amount": "2950.0", + "unit": "asset" + } + }, + { + "pair": [ + "USD", + "YGG" + ], + "minimalOrder": { + "amount": "11104.0", + "unit": "asset" + } + }, + { + "pair": [ + "ETH", + "YGG" + ], + "minimalOrder": { + "amount": "11104.0", + "unit": "asset" + } + }, + { + "pair": [ + "USD", + "MLN" + ], + "minimalOrder": { + "amount": "2.0", + "unit": "asset" + } + }, + { + "pair": [ + "ETH", + "MLN" + ], + "minimalOrder": { + "amount": "2.0", + "unit": "asset" + } + }, + { + "pair": [ + "USD", + "WTC" + ], + "minimalOrder": { + "amount": "4.0", + "unit": "asset" + } + }, + { + "pair": [ + "ETH", + "WTC" + ], + "minimalOrder": { + "amount": "4.0", + "unit": "asset" + } + }, + { + "pair": [ + "USD", + "CSX" + ], + "minimalOrder": { + "amount": "72.0", + "unit": "asset" + } + }, + { + "pair": [ + "ETH", + "CSX" + ], + "minimalOrder": { + "amount": "72.0", + "unit": "asset" + } + }, + { + "pair": [ + "USD", + "OMN" + ], + "minimalOrder": { + "amount": "2.0", + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "OMN" + ], + "minimalOrder": { + "amount": "2.0", + "unit": "asset" + } + }, + { + "pair": [ + "USD", + "INT" + ], + "minimalOrder": { + "amount": "352.0", + "unit": "asset" + } + }, + { + "pair": [ + "ETH", + "INT" + ], + "minimalOrder": { + "amount": "352.0", + "unit": "asset" + } + }, + { + "pair": [ + "USD", + "DRN" + ], + "minimalOrder": { + "amount": "62.0", + "unit": "asset" + } + }, + { + "pair": [ + "ETH", + "DRN" + ], + "minimalOrder": { + "amount": "62.0", + "unit": "asset" + } + }, + { + "pair": [ + "USD", + "PNK" + ], + "minimalOrder": { + "amount": "1394.0", + "unit": "asset" + } + }, + { + "pair": [ + "ETH", + "PNK" + ], + "minimalOrder": { + "amount": "1394.0", + "unit": "asset" + } + }, + { + "pair": [ + "USD", + "DGB" + ], + "minimalOrder": { + "amount": "566.0", + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "DGB" + ], + "minimalOrder": { + "amount": "566.0", + "unit": "asset" + } + }, + { + "pair": [ + "USD", + "BSV" + ], + "minimalOrder": { + "amount": "0.1", + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "BSV" + ], + "minimalOrder": { + "amount": "0.1", + "unit": "asset" + } + }, + { + "pair": [ + "USD", + "BAB" + ], + "minimalOrder": { + "amount": "0.04", + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "BAB" + ], + "minimalOrder": { + "amount": "0.04", + "unit": "asset" + } + } + ] +} \ No newline at end of file diff --git a/exchange/wrappers/bitfinex.js b/exchange/wrappers/bitfinex.js new file mode 100644 index 0000000..cb82e70 --- /dev/null +++ b/exchange/wrappers/bitfinex.js @@ -0,0 +1,319 @@ + +const Bitfinex = require("bitfinex-api-node"); +const _ = require('lodash'); +const moment = require('moment'); + +const Errors = require('../exchangeErrors'); +const retry = require('../exchangeUtils').retry; + +const marketData = require('./bitfinex-markets.json'); + +var Trader = function(config) { + _.bindAll(this); + if(_.isObject(config)) { + this.key = config.key; + this.secret = config.secret; + } + this.name = 'Bitfinex'; + this.balance; + this.price; + this.asset = config.asset; + this.currency = config.currency; + this.pair = this.asset + this.currency; + this.bitfinex = new Bitfinex.RESTv1({apiKey: this.key, apiSecret: this.secret, transform: true}); + + this.interval = 4000; +} + +const includes = (str, list) => { + if(!_.isString(str)) + return false; + + return _.some(list, item => str.includes(item)); +} + +const recoverableErrors = [ + 'SOCKETTIMEDOUT', + 'ESOCKETTIMEDOUT', + 'TIMEDOUT', + 'CONNRESET', + 'CONNREFUSED', + 'NOTFOUND', + '443', + '504', + '503', + '502', + 'Empty response', + 'Nonce is too small' +]; + +Trader.prototype.handleResponse = function(funcName, callback) { + return (error, data) => { + + if(!error && _.isEmpty(data)) { + error = new Error('Empty response'); + } + + if(error) { + const message = error.message; + + console.log('handleResponse', funcName, message); + + // in case we just cancelled our balances might not have + // settled yet, retry. + if( + funcName === 'submitOrder' && + message.includes('not enough exchange balance') + ) { + error.retry = 20; + return callback(error); + } + + // most likely problem with v1 api + if( + funcName === 'submitOrder' && + message.includes('Cannot evaluate your available balance, please try again') + ) { + error.retry = 10; + return callback(error); + } + + // in some situations bfx returns 404 on + // orders created recently + if( + funcName === 'checkOrder' && + message.includes('Not Found') + ) { + error.retry = 5; + return callback(error); + } + + if(includes(message, recoverableErrors)) { + error.notFatal = true; + return callback(error); + } + + if(includes(message, 'Too Many Requests')) { + error.notFatal = true; + error.backoffDelay = 5000; + } + } + + return callback(error, data); + } +}; + +Trader.prototype.getPortfolio = function(callback) { + const processResponse = (err, data) => { + if (err) return callback(err); + + // We are only interested in funds in the "exchange" wallet + data = data.filter(c => c.type === 'exchange'); + + const asset = _.find(data, c => c.currency.toUpperCase() === this.asset); + const currency = _.find(data, c => c.currency.toUpperCase() === this.currency); + + let assetAmount, currencyAmount; + + if(_.isObject(asset) && _.isNumber(+asset.available) && !_.isNaN(+asset.available)) + assetAmount = +asset.available; + else { + assetAmount = 0; + } + + if(_.isObject(currency) && _.isNumber(+currency.available) && !_.isNaN(+currency.available)) + currencyAmount = +currency.available; + else { + currencyAmount = 0; + } + + const portfolio = [ + { name: this.asset, amount: assetAmount }, + { name: this.currency, amount: currencyAmount }, + ]; + + callback(undefined, portfolio); + }; + + const fetch = cb => this.bitfinex.wallet_balances(this.handleResponse('getPortfolio', cb)); + retry(null, fetch, processResponse); +} + +Trader.prototype.getTicker = function(callback) { + const processResponse = (err, data) => { + if (err) + return callback(err); + + callback(undefined, {bid: +data.bid, ask: +data.ask}); + }; + + const fetch = cb => this.bitfinex.ticker(this.pair, this.handleResponse('getTicker', cb)); + retry(null, fetch, processResponse); +} + +Trader.prototype.getFee = function(callback) { + const makerFee = 0.1; + // const takerFee = 0.2; + callback(undefined, makerFee / 100); +} + +Trader.prototype.roundAmount = function(amount) { + return Math.floor(amount*100000000)/100000000; +} + +Trader.prototype.roundPrice = function(price) { + // todo: calc significant digits + return price; +} + +Trader.prototype.submitOrder = function(type, amount, price, callback) { + const processResponse = (err, data) => { + if (err) + return callback(err); + + callback(null, data.order_id); + } + + const fetch = cb => this.bitfinex.new_order(this.pair, + amount + '', + price + '', + this.name.toLowerCase(), + type, + 'exchange limit', + this.handleResponse('submitOrder', cb) + ); + + retry(null, fetch, processResponse); +} + +Trader.prototype.buy = function(amount, price, callback) { + this.submitOrder('buy', amount, price, callback); +} + +Trader.prototype.sell = function(amount, price, callback) { + this.submitOrder('sell', amount, price, callback); +} + +Trader.prototype.checkOrder = function(order_id, callback) { + const processResponse = (err, data) => { + if (err) { + console.log('this is after we have retried fetching it'); + // this is after we have retried fetching it + // in this.handleResponse. + if(err.message.includes('Not Found')) { + return callback(undefined, { + open: false, + executed: true + }); + } + + return callback(err); + } + + return callback(undefined, { + open: data.is_live, + executed: data.original_amount === data.executed_amount, + filledAmount: +data.executed_amount + }); + } + + const fetcher = cb => this.bitfinex.order_status(order_id, this.handleResponse('checkOrder', cb)); + retry(null, fetcher, processResponse); +} + + +Trader.prototype.getOrder = function(order_id, callback) { + const processResponse = (err, data) => { + if (err) return callback(err); + + var price = parseFloat(data.avg_execution_price); + var amount = parseFloat(data.executed_amount); + var date = moment.unix(data.timestamp); + + console.log('getOrder', data); + + // TEMP: Thu May 31 14:49:34 CEST 2018 + // the `past_trades` call is not returning + // any data. + return callback(undefined, {price, amount, date}); + + const processPastTrade = (err, data) => { + if (err) return callback(err); + + console.log('processPastTrade', data); + const trade = _.first(data); + + const fees = { + [trade.fee_currency]: trade.fee_amount + } + + callback(undefined, {price, amount, date, fees}); + } + + // we need another API call to fetch the fees + const feeFetcher = cb => this.bitfinex.past_trades(this.currency, {since: data.timestamp}, this.handleResponse('pastTrades', cb)); + retry(null, feeFetcher, processPastTrade); + + callback(undefined, {price, amount, date}); + }; + + const fetcher = cb => this.bitfinex.order_status(order_id, this.handleResponse('getOrder', cb)); + retry(null, fetcher, processResponse); +} + + +Trader.prototype.cancelOrder = function(order_id, callback) { + const processResponse = (err, data) => { + if (err) { + return callback(err); + } + + return callback(undefined, false); + } + + const handler = cb => this.bitfinex.cancel_order(order_id, this.handleResponse('cancelOrder', cb)); + retry(null, handler, processResponse); +} + +Trader.prototype.getTrades = function(since, callback, descending) { + const processResponse = (err, data) => { + if (err) return callback(err); + + var trades = _.map(data, function(trade) { + return { + tid: trade.tid, + date: trade.timestamp, + price: +trade.price, + amount: +trade.amount + } + }); + + callback(undefined, descending ? trades : trades.reverse()); + }; + + var path = this.pair; + if(since) + path += '?limit_trades=2000'; + + const handler = cb => this.bitfinex.trades(path, this.handleResponse('getTrades', cb)); + retry(null, handler, processResponse); +} + +Trader.getCapabilities = function () { + return { + name: 'Bitfinex', + slug: 'bitfinex', + currencies: marketData.currencies, + assets: marketData.assets, + markets: marketData.markets, + requires: ['key', 'secret'], + tid: 'tid', + providesFullHistory: true, + providesHistory: 'date', + tradable: true, + forceReorderDelay: true, + gekkoBroker: 0.6 + }; +} + +module.exports = Trader; diff --git a/exchange/wrappers/bitfinex_v2.js.prep b/exchange/wrappers/bitfinex_v2.js.prep new file mode 100644 index 0000000..bc3aa01 --- /dev/null +++ b/exchange/wrappers/bitfinex_v2.js.prep @@ -0,0 +1,246 @@ +// NOT USED, see: https://github.com/bitfinexcom/bitfinex-api-node/issues/321 + +const Bitfinex = require("bitfinex-api-node"); +const _ = require('lodash'); +const moment = require('moment'); + +const Errors = require('../exchangeErrors'); +const retry = require('../exchangeUtils').retry; + +const marketData = require('./bitfinex-markets.json'); + +var Trader = function(config) { + _.bindAll(this); + if(_.isObject(config)) { + this.key = config.key; + this.secret = config.secret; + } + this.name = 'Bitfinex'; + this.balance; + this.price; + this.asset = config.asset; + this.currency = config.currency; + this.pair = 't' + this.asset + this.currency; + this.bitfinex = new Bitfinex.RESTv2({apiKey: this.key, apiSecret: this.secret, transform: true}); +} + +const includes = (str, list) => { + if(!_.isString(str)) + return false; + + return _.some(list, item => str.includes(item)); +} + +// Probably we need to update these string +const recoverableErrors = [ + 'SOCKETTIMEDOUT', + 'TIMEDOUT', + 'CONNRESET', + 'CONNREFUSED', + 'NOTFOUND', + '429', + '443', + '504', + '503', + '500', + '502', + 'Empty response' +]; + +Trader.prototype.handleResponse = function(funcName, callback) { + return (error, data) => { + if(!error && _.isEmpty(data)) { + error = new Error('Empty response'); + } + + if(error) { + const message = error.message || error; + + if(!includes(message, recoverableErrors)) { + const error = new Error(message); + error.notFatal = true; + return callback(error); + } + } + + return callback(error, data); + } +}; + +Trader.prototype.getPortfolio = function(callback) { + const processResponse = (err, data) => { + console.log('processResponse', {err, data}); + if (err) return callback(err); + + // We are only interested in funds in the "exchange" wallet + data = data.filter(c => c.type === 'exchange'); + + const asset = _.find(data, c => c.currency.toUpperCase() === this.asset); + const currency = _.find(data, c => c.currency.toUpperCase() === this.currency); + + console.log(currency.balance); + + let assetAmount, currencyAmount; + + if(_.isObject(asset) && _.isNumber(+asset.balanceAvailable) && !_.isNaN(+asset.balanceAvailable)) + assetAmount = +asset.balanceAvailable; + else { + assetAmount = 0; + } + + if(_.isObject(currency) && _.isNumber(+currency.balanceAvailable) && !_.isNaN(+currency.balanceAvailable)) + currencyAmount = +currency.balanceAvailable; + else { + currencyAmount = 0; + } + + const portfolio = [ + { name: this.asset, amount: assetAmount }, + { name: this.currency, amount: currencyAmount }, + ]; + + callback(undefined, portfolio); + }; + + const fetch = cb => this.bitfinex.wallets(this.handleResponse('getPortfolio', cb)); + retry(null, fetch, processResponse); +} + +Trader.prototype.getTicker = function(callback) { + const processResponse = (err, data) => { + if (err) + return callback(err); + + callback(undefined, {bid: +data.bid, ask: +data.ask}); + }; + + const fetch = cb => this.bitfinex.ticker(this.pair, this.handleResponse('getTicker', cb)); + retry(null, fetch, processResponse); +} + +Trader.prototype.getFee = function(callback) { + const makerFee = 0.1; + // const takerFee = 0.2; + callback(undefined, makerFee / 100); +} + +Trader.prototype.roundAmount = function(amount) { + return Math.floor(amount*100000000)/100000000; +} + +Trader.prototype.roundPrice = function(price) { + // todo: calc significant digits + return price; +} + +Trader.prototype.submit_order = function(type, amount, price, callback) { + const processResponse = (err, data) => { + if (err) return callback(err); + + callback(err, data.order_id); + } + + const fetcher = cb => this.bitfinex.new_order(this.pair, + amount + '', + price + '', + this.name.toLowerCase(), + type, + 'exchange limit', + this.handleResponse('submitOrder', cb) + ); + + retry(retryCritical, fetcher, processResponse); +} + +Trader.prototype.buy = function(amount, price, callback) { + this.submit_order('buy', amount, price, callback); +} + +Trader.prototype.sell = function(amount, price, callback) { + this.submit_order('sell', amount, price, callback); +} + +Trader.prototype.checkOrder = function(order_id, callback) { + const processResponse = (err, data) => { + if (err) return callback(err); + + return callback(undefined, { + open: data.is_live, + executed: data.original_amount === data.executed_amount, + filled: +data.executed_amount + }); + } + + const fetcher = cb => this.bitfinex.order_status(order_id, this.handleResponse('checkOrder', cb)); + retry(retryCritical, fetcher, processResponse); +} + + +Trader.prototype.getOrder = function(order_id, callback) { + const processResponse = (err, data) => { + if (err) return callback(err); + + var price = parseFloat(data.avg_execution_price); + var amount = parseFloat(data.executed_amount); + var date = moment.unix(data.timestamp); + + callback(undefined, {price, amount, date}); + }; + + const fetcher = cb => this.bitfinex.order_status(order_id, this.handleResponse('getOrder', cb)); + retry(retryCritical, fetcher, processResponse); +} + + +Trader.prototype.cancelOrder = function(order_id, callback) { + const processResponse = (err, data) => { + if (err) return callback(err); + + return callback(undefined); + } + + const handler = cb => this.bitfinex.cancel_order(order_id, this.handleResponse('cancelOrder', cb)); + retry(retryForever, handler, processResponse); +} + +Trader.prototype.getTrades = function(since, callback, descending) { + const processResponse = (err, data) => { + if (err) return callback(err); + + var trades = _.map(data, function(trade) { + return { + tid: trade.tid, + date: trade.timestamp, + price: +trade.price, + amount: +trade.amount + } + }); + + callback(undefined, descending ? trades : trades.reverse()); + }; + + var path = this.pair; + if(since) + path += '?limit_trades=2000'; + + const handler = cb => this.bitfinex.trades(path, this.handleResponse('getTrades', cb)); + retry(retryForever, handler, processResponse); +} + +Trader.getCapabilities = function () { + return { + name: 'Bitfinex', + slug: 'bitfinex', + currencies: marketData.currencies, + assets: marketData.assets, + markets: marketData.markets, + requires: ['key', 'secret'], + tid: 'tid', + providesFullHistory: true, + providesHistory: 'date', + tradable: true, + forceReorderDelay: true + }; +} + +module.exports = Trader; diff --git a/exchange/wrappers/bitstamp.js.old b/exchange/wrappers/bitstamp.js.old new file mode 100644 index 0000000..6c57173 --- /dev/null +++ b/exchange/wrappers/bitstamp.js.old @@ -0,0 +1,261 @@ +var Bitstamp = require("bitstamp"); +var _ = require('lodash'); +var moment = require('moment'); +var log = require('../core/log'); +var util = require('../core/util'); + +var Trader = function(config) { + _.bindAll(this); + if(_.isObject(config)) { + this.key = config.key; + this.secret = config.secret; + this.clientID = config.username; + this.asset = config.asset.toLowerCase(); + this.currency = config.currency.toLowerCase(); + this.market = this.asset + this.currency; + } + this.name = 'Bitstamp'; + + this.bitstamp = new Bitstamp(this.key, this.secret, this.clientID); +} + +// if the exchange errors we try the same call again after +// waiting 10 seconds +Trader.prototype.retry = function(method, args) { + var wait = +moment.duration(10, 'seconds'); + log.debug(this.name, 'returned an error, retrying..'); + + var self = this; + + // make sure the callback (and any other fn) + // is bound to Trader + _.each(args, function(arg, i) { + if(_.isFunction(arg)) + args[i] = _.bind(arg, self); + }); + + // run the failed method again with the same + // arguments after wait + setTimeout( + function() { method.apply(self, args) }, + wait + ); +} + +Trader.prototype.getPortfolio = function(callback) { + var args = _.toArray(arguments); + var set = function(err, data) { + + if(data && data.error) { + err = data.error; + } + + if(err) { + if(err.meta && err.meta.reason === 'API key not found') + util.die('Bitstamp says this API keys is invalid..'); + + log.error('BITSTAMP API ERROR:', err); + return this.retry(this.getPortfolio, args); + } + + var portfolio = []; + _.each(data, function(amount, asset) { + if(asset.indexOf('available') !== -1) { + asset = asset.substr(0, 3).toUpperCase(); + portfolio.push({name: asset, amount: parseFloat(amount)}); + } + }); + callback(err, portfolio); + }.bind(this); + + this.bitstamp.balance(this.market, set); +} + +Trader.prototype.getTicker = function(callback) { + this.bitstamp.ticker(this.market, callback); +} + +Trader.prototype.getFee = function(callback) { + var set = function(err, data) { + if(err) + callback(err); + + callback(false, data.fee / 100); + }.bind(this); + + this.bitstamp.balance(this.market, set); +} + +Trader.prototype.buy = function(amount, price, callback) { + var findMarket = function(pair) { + return pair.pair[0].toLowerCase() === this.currency && pair.pair[1].toLowerCase() === this.asset + } + var args = _.toArray(arguments); + var set = function(err, result) { + if(err || result.status === "error") { + log.error('unable to buy:', err, result.reason, 'retrying...'); + return this.retry(this.buy, args); + } + + callback(null, result.id); + }.bind(this); + + //Decrease amount by 1% to avoid trying to buy more than balance allows. + amount = Number(amount * 0.99).toFixed(8); + + // Use proper precision by currency pair + var pair = _.find(Trader.getCapabilities().markets, _.bind(findMarket, this)); + price = Number(Number.parseFloat(price).toFixed(pair.precision)); + + this.bitstamp.buy(this.market, amount, price, undefined, set); +} + +Trader.prototype.sell = function(amount, price, callback) { + var findMarket = function(pair) { + return pair.pair[0].toLowerCase() === this.currency && pair.pair[1].toLowerCase() === this.asset + } + var args = _.toArray(arguments); + var set = function(err, result) { + if(err || result.status === "error") { + log.error('unable to sell:', err, result.reason, 'retrying...'); + return this.retry(this.sell, args); + } + + callback(null, result.id); + }.bind(this); + + // prevent: + // 'Ensure that there are no more than 8 decimal places.' + amount = Number(Number.parseFloat(amount)).toFixed(8); + + // Use proper precision by currency pair + var pair = _.find(Trader.getCapabilities().markets, _.bind(findMarket, this)); + price = Number(Number.parseFloat(price).toFixed(pair.precision)); + + this.bitstamp.sell(this.market, amount, price, undefined, set); +} + + +Trader.prototype.getOrder = function(id, callback) { + var args = _.toArray(arguments); + var get = function(err, data) { + if(!err && _.isEmpty(data) && _.isEmpty(data.result)) + err = 'no data'; + + else if(!err && !_.isEmpty(data.error)) + err = data.error; + + if(err) { + log.error('Unable to get order', order, JSON.stringify(err)); + return this.retry(this.getOrder, args); + } + + var order = _.find(data, o => o.order_id === +id); + + if(!order) { + // if the order was cancelled we are unable + // to retrieve it, assume that this is what + // is happening. + return callback(err, { + price: 0, + amount: 0, + date: moment(0) + }); + } + + var price = parseFloat( order[`${this.asset}_${this.currency}`] ); + var amount = Math.abs(parseFloat( order[this.asset] )); + var date = moment( order.datetime ); + + callback(err, {price, amount, date}); + }.bind(this); + + this.bitstamp.user_transactions(this.market, {}, get); +} + +Trader.prototype.checkOrder = function(order, callback) { + var check = function(err, result) { + var stillThere = _.find(result, function(o) { return o.id === order }); + callback(err, !stillThere); + }.bind(this); + + this.bitstamp.open_orders(this.market, check); +} + +Trader.prototype.cancelOrder = function(order, callback) { + var args = _.toArray(arguments); + var cancel = function(err, result) { + if(err || !result) { + log.error('unable to cancel order', order, '(', err, result, ')'); + return this.retry(this.cancelOrder, args); + } + + callback(); + }.bind(this); + + this.bitstamp.cancel_order(order, cancel); +} + +Trader.prototype.getTrades = function(since, callback, descending) { + var args = _.toArray(arguments); + var process = function(err, trades) { + if(err) + return this.retry(this.getTrades, args); + + var result = _.map(trades, t => { + return { + date: t.date, + tid: +t.tid, + price: +t.price, + amount: +t.amount + } + }) + + callback(null, result.reverse()); + }.bind(this); + + // NOTE: temporary disabled, see https://github.com/askmike/gekko/issues/794 + // if(since) + // this.bitstamp.transactions(this.market, {time: 'day'}, process); + // else + this.bitstamp.transactions(this.market, process); +} + +Trader.getCapabilities = function () { + return { + name: 'Bitstamp', + slug: 'bitstamp', + currencies: ['USD', 'EUR', 'BTC'], + assets: ['BTC', 'BCH', 'EUR', 'LTC', 'ETH', 'XRP'], + maxTradesAge: 60, + maxHistoryFetch: null, + markets: [ + { pair: ['USD', 'EUR'], minimalOrder: { amount: 5, unit: 'currency' }, precision: 2 }, + + { pair: ['USD', 'BTC'], minimalOrder: { amount: 5, unit: 'currency' }, precision: 2 }, + { pair: ['EUR', 'BTC'], minimalOrder: { amount: 5, unit: 'currency' }, precision: 2 }, + + { pair: ['USD', 'BCH'], minimalOrder: { amount: 5, unit: 'currency' }, precision: 2 }, + { pair: ['EUR', 'BCH'], minimalOrder: { amount: 5, unit: 'currency' }, precision: 2 }, + { pair: ['BTC', 'BCH'], minimalOrder: { amount: 0.001, unit: 'currency'}, precision: 8 }, + + { pair: ['USD', 'XRP'], minimalOrder: { amount: 5, unit: 'currency' }, precision: 5 }, + { pair: ['EUR', 'XRP'], minimalOrder: { amount: 5, unit: 'currency' }, precision: 5 }, + { pair: ['BTC', 'XRP'], minimalOrder: { amount: 0.001, unit: 'currency'}, precision: 8 }, + + { pair: ['USD', 'LTC'], minimalOrder: { amount: 5, unit: 'currency' }, precision: 2 }, + { pair: ['EUR', 'LTC'], minimalOrder: { amount: 5, unit: 'currency' }, precision: 2 }, + { pair: ['BTC', 'LTC'], minimalOrder: { amount: 0.001, unit: 'currency'}, precision: 8 }, + + { pair: ['USD', 'ETH'], minimalOrder: { amount: 5, unit: 'currency' }, precision: 2 }, + { pair: ['EUR', 'ETH'], minimalOrder: { amount: 5, unit: 'currency' }, precision: 2 }, + { pair: ['BTC', 'ETH'], minimalOrder: { amount: 0.001, unit: 'currency'}, precision: 8 }, + ], + requires: ['key', 'secret', 'username'], + fetchTimespan: 60, + tid: 'tid', + tradable: true + }; +} + +module.exports = Trader; diff --git a/exchange/wrappers/bittrex.js b/exchange/wrappers/bittrex.js new file mode 100644 index 0000000..d0fb18e --- /dev/null +++ b/exchange/wrappers/bittrex.js @@ -0,0 +1,633 @@ +const Bittrex = require('node.bittrex.api'); +const _ = require('lodash'); +const moment = require('moment'); +const retry = require('../exchangeUtils').retry; + +// Helper methods +function joinCurrencies(currencyA, currencyB){ + return currencyA + '-' + currencyB; +} + +var Trader = function(config) { + this.currency = config.currency; + this.asset = config.asset; + + if(!config.key) { + // no api key defined -> we need to set a + // dummy key, otherwise the Bittrex module + // will not work even for public requests. + this.key = 'dummyApiKey'; + this.secret = 'dummyApiKey'; + } else { + this.key = config.key; + this.secret = config.secret; + } + + this.name = 'Bittrex'; + this.balance; + this.price; + + this.pair = [this.currency, this.asset].join('-'); + + Bittrex.options({ + apikey: this.key, + apisecret: this.secret, + stream: false, + verbose: false, + cleartext: false, + inverse_callback_arguments: true + }); + + this.bittrexApi = Bittrex; +} + +const recoverableErrors = [ + 'SOCKETTIMEDOUT', + 'TIMEDOUT', + 'CONNRESET', + 'CONNREFUSED', + 'NOTFOUND', +]; + +const includes = (str, list) => { + if(!_.isString(str)) + return false; + + return _.some(list, item => str.includes(item)); +} + +Trader.prototype.processResponse = function(callback) { + return (err, response) => { + + if(err) { + if(!_.isError(err)) { + err = new Error(err.message); + } + + if(err.message === 'APIKEY_INVALID') { + console.log('APIKEY_INVALID'); + err.retry = 10; + } + + if(includes(err.message, recoverableErrors)) { + err.notFatal = true; + } + + return callback(err); + } + + callback(undefined, response); + } +} + +Trader.prototype.getPortfolio = function(callback) { + const handle = (err, data) => { + if(err) { + return callback(err); + } + + data = data.result; + + let assetEntry = _.find(data, i => i.Currency == this.asset); + let currencyEntry = _.find(data, i => i.Currency == this.currency); + + if(_.isUndefined(assetEntry)) { + assetEntry = { + Available: 0.0, + Currency: this.asset + } + } + + if(_.isUndefined(currencyEntry)) { + currencyEntry = { + Available: 0.0, + Currency: this.currency + } + } + + const assetAmount = parseFloat( assetEntry.Available ); + const currencyAmount = parseFloat( currencyEntry.Available ); + + const portfolio = [ + { name: this.asset, amount: assetAmount }, + { name: this.currency, amount: currencyAmount } + ]; + + callback(undefined, portfolio); + } + + const fetch = next => this.bittrexApi.getbalances(this.processResponse(next)); + retry(null, fetch, handle); +} + +Trader.prototype.roundPrice = function(price) { + return _.round(price, 8); +} + +Trader.prototype.roundAmount = function(price) { + return _.floor(price, 8); +} + +Trader.prototype.getTicker = function(callback) { + const handle = (err, data) => { + if(err) { + return callback(err); + } + + const tick = data.result; + + callback(null, { + bid: parseFloat(tick.Bid), + ask: parseFloat(tick.Ask), + }) + } + + const fetch = next => this.bittrexApi.getticker({market: this.pair}, this.processResponse(next)); + retry(null, fetch, handle); +} + +Trader.prototype.getFee = function(callback) { + callback(false, 0.00025); +} + +Trader.prototype.buy = function(amount, price, callback) { + const handle = (err, result) => { + console.log('[bittrex buy]', err, result); + + if(err) { + return callback(err); + } + + const id = _.get(result, 'result.uuid'); + if(!id) { + console.log('[bittrex buy error]', result); + return callback(new Error('Bad response')); + } + + callback(undefined, id); + } + + const fetch = next => this.bittrexApi.buylimit({market: this.pair, quantity: amount, rate: price}, this.processResponse(next)); + retry(null, fetch, handle); +} + +Trader.prototype.sell = function(amount, price, callback) { + const handle = (err, result) => { + console.log('[bittrex sell]', err, result); + + if(err) { + return callback(err); + } + + const id = _.get(result, 'result.uuid'); + if(!id) { + console.log('[bittrex sell error]', result); + return callback(new Error('Bad response')); + } + + callback(undefined, id); + } + + const fetch = next => this.bittrexApi.selllimit({market: this.pair, quantity: amount, rate: price}, this.processResponse(next)); + retry(null, fetch, handle); +} + +Trader.prototype.checkOrder = function(order, callback) { + const handle = (err, result) => { + + if(err) { + return callback(err); + } + + console.log('checkOrder', result); + throw 'a'; + } + + const fetch = next => this.bittrexApi.getopenorders({market: this.pair}, this.processResponse(next)); + retry(null, fetch, handle); +} + +Trader.prototype.getOrder = function(order, callback) { + + const handle = (err, result) => { + console.log('getOrder', result); + + if(err) + return callback(err); + + let price = 0; + let amount = 0; + let date = moment(0); + + if(!result.success) { + return callback(null, {price, amount, date}); + } + + const resultOrder = result.result; + + price = resultOrder.Price; + amount = resultOrder.Quantity; + + if(resultOrder.IsOpen) { + date = moment(resultOrder.Opened); + } else { + date = moment(resultOrder.Closed); + } + + callback(err, {price, amount, date}); + } + + const fetch = next => this.bittrexApi.getorder({uuid: order}, this.processResponse(next)); + retry(null, fetch, handle); +} + +Trader.prototype.cancelOrder = function(order, callback) { + const handle = (err, result) => { + console.log('cancelOrder', err, result); + if(err) { + return callback(err); + } + + throw 'a'; + + // if(!result.success && result.message === 'ORDER_NOT_OPEN') { + // log.debug('getOrder', 'ORDER_NOT_OPEN: assuming already closed or executed'); + // } + + callback(undefined, true); + } + + const fetch = next => this.bittrexApi.cancel({uuid: order}, this.processResponse(next)); + retry(null, fetch, handle); +} + +Trader.prototype.getTrades = function(since, callback, descending) {; + var firstFetch = !!since; + + const handle = (err, data) => { + if(err) { + return callback(err); + } + + var result = data.result; + + // Edge case, see here: + // @link https://github.com/askmike/gekko/issues/479 + if(firstFetch && _.size(result) === 50000) + util.die( + [ + 'Bittrex did not provide enough data. Read this:', + 'https://github.com/askmike/gekko/issues/479' + ].join('\n\n') + ); + + result = _.map(result, function(trade) { + var mr = { + tid: trade.Id, + amount: +trade.Quantity, + date: moment.utc(trade.TimeStamp).unix(), + timeStamp: trade.TimeStamp, + price: +trade.Price + }; + return mr; + }); + + callback(null, result.reverse()); + } + + var params = { + currencyPair: joinCurrencies(this.currency, this.asset) + } + + if(since) { + params.start = since.unix(); + } + + const fetch = next => this.bittrexApi.getmarkethistory({ market: params.currencyPair }, this.processResponse(next)); + retry(null, fetch, handle); +} + +Trader.getCapabilities = function() { + return { + name: 'bittrex', + slug: 'bittrex', + currencies: ['BITCNY', 'BTC', 'ETH', 'USDT'], + assets: [ + '1ST', '2GIVE', '8BIT', 'ABY', 'ADC', 'ADT', 'ADX', 'AEON', 'AGRS', 'AM', + 'AMP', 'AMS', 'ANS', 'ANT', 'APEX', 'APX', 'ARB', 'ARCH', 'ARDR', 'ARK', + 'AUR', 'BAT', 'BAY', 'BCC', 'BCY', 'BITB', 'BITCNY', 'BITS', 'BITZ', 'BLC', + 'BLITZ', 'BLK', 'BLOCK', 'BNT', 'BOB', 'BRK', 'BRX', 'BSD', 'BSTY', 'BTA', + 'BTC', 'BTCD', 'BTS', 'BURST', 'BYC', 'CANN', 'CCN', 'CFI', 'CLAM', + 'CLOAK', 'CLUB', 'COVAL', 'CPC', 'CRB', 'CRBIT', 'CRW', 'CRYPT', 'CURE', + 'CVC', 'DAR', 'DASH', 'DCR', 'DCT', 'DGB', 'DGC', 'DGD', 'DMD', 'DOGE', + 'DOPE', 'DRACO', 'DTB', 'DTC', 'DYN', 'EBST', 'EDG', 'EFL', 'EGC', 'EMC', + 'EMC2', 'ENRG', 'ERC', 'ETC', 'ETH', 'EXCL', 'EXP', 'FAIR', 'FC2', 'FCT', + 'FLDC', 'FLO', 'FRK', 'FSC2', 'FTC', 'FUN', 'GAM', 'GAME', 'GBG', 'GBYTE', + 'GCR', 'GEMZ', 'GEO', 'GHC', 'GLD', 'GNO', 'GNT', 'GOLOS', 'GP', 'GRC', + 'GRS', 'GRT', 'GUP', 'HKG', 'HMQ', 'HYPER', 'HZ', 'INCNT', 'INFX', 'IOC', + 'ION', 'IOP', 'J', 'JWL', 'KMD', 'KORE', 'KR', 'LBC', 'LGD', 'LMC', 'LSK', + 'LTC', 'LUN', 'LXC', 'MAID', 'MAX', 'MCO', 'MEC', 'MEME', 'METAL', 'MLN', + 'MND', 'MONA', 'MTL', 'MTR', 'MUE', 'MUSIC', 'MYR', 'MYST', 'MZC', 'NAUT', + 'NAV', 'NBT', 'NEO', 'NET', 'NEU', 'NLG', 'NMR', 'NTRN', 'NXC', 'NXS', + 'NXT', 'OC', 'OK', 'OMG', 'OMNI', 'ORB', 'PART', 'PAY', 'PDC', 'PINK', + 'PIVX', 'PKB', 'POT', 'PPC', 'PRIME', 'PTC', 'PTOY', 'PXI', 'QRL', 'QTL', + 'QTUM', 'QWARK', 'RADS', 'RBY', 'RDD', 'REP', 'RISE', 'RLC', 'ROOT', + 'SBD', 'SC', 'SCOT', 'SCRT', 'SDC', 'SEC', 'SEQ', 'SFR', 'SHIFT', 'SIB', + 'SLG', 'SLING', 'SLR', 'SLS', 'SNGLS', 'SNRG', 'SNT', 'SOON', 'SPHR', + 'SPR', 'SPRTS', 'SSD', 'START', 'STEEM', 'STEPS', 'STORJ', 'STRAT', 'STV', + 'SWIFT', 'SWING', 'SWT', 'SYNX', 'SYS', 'TES', 'THC', 'TIME', 'TIT', + 'TKN', 'TKS', 'TRI', 'TRIG', 'TRK', 'TROLL', 'TRST', 'TRUST', 'TX', 'U', + 'UBQ', 'UFO', 'UNB', 'UNIQ', 'UNIT', 'UNO', 'USDT', 'UTC', 'VIA', 'VIOR', + 'VIRAL', 'VOX', 'VPN', 'VRC', 'VRM', 'VTC', 'VTR', 'WARP', 'WAVES', 'WBB', + 'WINGS', 'XAUR', 'XBB', 'XC', 'XCO', 'XCP', 'XDN', 'XDQ', 'XEL', 'XEM', + 'XLM', 'XMG', 'XMR', 'XPY', 'XQN', 'XRP', 'XSEED', 'XST', 'XTC', 'XVC', + 'XVG', 'XWC', 'XZC', 'YBC', 'ZCL', 'ZEC', 'ZEN','BTG','OMG','ADA' + ], + markets: [ + + // *** BTCNY <-> XXX + { pair: ['BITCNY','BTC'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + + // *** BTC <-> XXX + { pair: ['BTC','1ST'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','2GIVE'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','ABY'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','ADT'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','ADX'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','AEON'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','AGRS'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','AMP'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','ANT'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','APX'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','ARDR'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','ARK'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','AUR'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','BAT'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','BAY'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','BCC'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','BCY'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','BITB'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','BLITZ'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','BLK'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','BLOCK'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','BNT'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','BRK'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','BRX'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','BSD'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','BTA'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','BTCD'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','BTS'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','BURST'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','BYC'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','CANN'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','CFI'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','CLAM'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','CLOAK'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','CLUB'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','COVAL'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','CPC'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','CRB'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','CRW'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','CURE'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','CVC'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','DAR'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','DASH'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','DCR'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','DCT'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','DGB'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','DGD'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','DMD'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','DOGE'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','DOPE'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','DRACO'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','DTB'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','DYN'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','EBST'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','EDG'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','EFL'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','EGC'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','EMC'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','EMC2'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','ENRG'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','ERC'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','ETC'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','ETH'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','EXCL'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','EXP'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','FAIR'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','FCT'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','FLDC'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','FLO'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','FTC'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','FUN'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','GAM'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','GAME'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','GBG'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','GBYTE'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','GCR'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','GEO'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','GLD'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','GNO'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','GNT'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','GOLOS'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','GRC'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','GRS'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','GUP'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','HKG'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','HMQ'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','INCNT'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','INFX'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','IOC'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','ION'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','IOP'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','KMD'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','KORE'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','LBC'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','LGD'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','LMC'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','LSK'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','LTC'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','LUN'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','MAID'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','MCO'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','MEME'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','MLN'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','MONA'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','MTL'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','MUE'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','MUSIC'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','MYST'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','NAUT'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','NAV'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','NBT'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','NEO'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','NLG'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','NMR'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','NXC'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','NXS'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','NXT'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','OK'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','OMG'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','OMNI'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','PART'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','PAY'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','PDC'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','PINK'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','PIVX'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','PKB'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','POT'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','PPC'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','PTC'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','PTOY'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','QRL'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','QTUM'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','QWARK'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','RADS'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','RBY'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','RDD'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','REP'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','RISE'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','RLC'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','SAFEX'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','SBD'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','SC'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','SEQ'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','SHIFT'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','SIB'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','SLR'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','SLS'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','SNGLS'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','SNRG'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','SNT'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','SPHR'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','SPR'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','START'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','STEEM'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','STORJ'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','STRAT'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','SWIFT'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','SWT'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','SYNX'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','SYS'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','THC'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','TIME'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','TKN'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','TKS'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','TRIG'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','TRST'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','TRUST'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','TX'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','UBQ'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','UNB'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','UNO'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','VIA'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','VOX'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','VRC'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','VRM'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','VTC'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','VTR'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','WAVES'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','WINGS'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','XAUR'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','XBB'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','XCP'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','XDN'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','XEL'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','XEM'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','XLM'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','XMG'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','XMR'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','XMY'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','XRP'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','XST'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','XVC'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','XVG'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','XWC'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','XZC'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','ZCL'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','ZEC'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['BTC','ZEN'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + + // *** ETH <-> XXX + { pair: ['ETH','1ST'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['ETH','ADT'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['ETH','ADX'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['ETH','ANT'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['ETH','BAT'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['ETH','BCC'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['ETH','BNT'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['ETH','BTS'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['ETH','CFI'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['ETH','CRB'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['ETH','CVC'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['ETH','DASH'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['ETH','DGB'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['ETH','DGD'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['ETH','ETC'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['ETH','FCT'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['ETH','FUN'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['ETH','GNO'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['ETH','GNT'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['ETH','GUP'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['ETH','HMQ'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['ETH','LGD'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['ETH','LTC'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['ETH','LUN'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['ETH','MCO'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['ETH','MTL'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['ETH','MYST'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['ETH','NEO'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['ETH','NMR'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['ETH','OMG'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['ETH','PAY'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['ETH','PTOY'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['ETH','QRL'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['ETH','QTUM'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['ETH','REP'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['ETH','RLC'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['ETH','SC'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['ETH','SNGLS'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['ETH','SNT'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['ETH','STORJ'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['ETH','STRAT'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['ETH','TIME'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['ETH','TKN'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['ETH','TRST'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['ETH','WAVES'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['ETH','WINGS'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['ETH','XEM'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['ETH','XLM'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['ETH','XMR'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['ETH','XRP'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['ETH','ZEC'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + + // *** USDT <-> XXX + { pair: ['USDT','BCC'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['USDT','BTC'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['USDT','DASH'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['USDT','ETC'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['USDT','ETH'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['USDT','LTC'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['USDT','NEO'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['USDT','XMR'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['USDT','XRP'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['USDT','ZEC'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['USDT','BTG'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['USDT','OMG'], minimalOrder: { amount: 0.00000001, unit: 'asset' }}, + { pair: ['USDT','ADA'], minimalOrder: { amount: 0.00000001, unit: 'asset' }} + + ], + requires: ['key', 'secret'], + tid: 'tid', + providesHistory: 'date', + providesFullHistory: false, + tradable: false, + forceReorderDelay: true, + gekkoBroker: '0.6.0' + }; +}; + + + + +module.exports = Trader; diff --git a/exchange/wrappers/btc-markets.js.old b/exchange/wrappers/btc-markets.js.old new file mode 100644 index 0000000..6b79e90 --- /dev/null +++ b/exchange/wrappers/btc-markets.js.old @@ -0,0 +1,249 @@ +var BTCMarkets = require('btc-markets'); +var _ = require('lodash'); +var moment = require('moment'); +var log = require('../core/log'); + +var Trader = function(config) { + _.bindAll(this); + if(_.isObject(config)) { + this.key = config.key; + this.secret = config.secret; + this.clientID = config.username; + this.currency = config.currency; + this.asset = config.asset; + } + this.name = 'BTC Markets'; + this.priceDivider = 100000000; // one hundred million + this.btcmakets = new BTCMarkets(this.key, this.secret); +} + +// if the exchange errors we try the same call again after +// waiting 10 seconds +Trader.prototype.retry = function(method, args) { + var wait = +moment.duration(10, 'seconds'); + log.debug(this.name, 'returned an error, retrying..'); + + var self = this; + + // make sure the callback (and any other fn) + // is bound to Trader + _.each(args, function(arg, i) { + if(_.isFunction(arg)) + args[i] = _.bind(arg, self); + }); + + // run the failed method again with the same + // arguments after wait + setTimeout( + function() { method.apply(self, args) }, + wait + ); +} + +Trader.prototype.getPortfolio = function(callback) { + var set = function(err, data) { + + if(!_.isEmpty(data.errorMessage)) + return callback('BTC-MARKET API ERROR: ' + data.errorMessage); + + var portfolio = _.map(data, function(balance) { + return { + name: balance.currency, + amount: balance.balance / this.priceDivider + } + }, this); + + callback(err, portfolio); + }.bind(this); + + this.btcmakets.getAccountBalances(set); +} + +Trader.prototype.getTicker = function(callback) { + var args = _.toArray(arguments); + var set = function(err, result) { + if(err) + return this.retry(this.getTicker, args); + + callback(null, { + bid: result.bestBid, + ask: result.bestAsk + }); + }.bind(this); + this.btcmakets.getTick(this.asset, this.currency, set); +} + +Trader.prototype.getFee = function(callback) { + var args = _.toArray(arguments); + var set = function(err, data) { + + if(!err && _.isEmpty(data)) + err = 'no data'; + else if(!err && !_.isEmpty(data.errorMessage)) + err = data.errorMessage; + if(err){ + log.error('unable to retrieve fee', err, ' retrying...'); + return this.retry(this.getFee, args); + } + data.tradingFeeRate /= this.priceDivider; + callback(false, data.tradingFeeRate); + }.bind(this); + this.btcmakets.getTradingFee(this.asset, this.currency, set); +} + +Trader.prototype.buy = function(amount, price, callback) { + + price *= this.priceDivider; + amount = Math.floor(amount * this.priceDivider); + var id = Math.random() + ''; + var set = function(err, data) { + if(!err && _.isEmpty(data)) + err = 'no data'; + else if(!err && !_.isEmpty(data.errorMessage)) + err = data.errorMessage; + if(err) + return log.error('unable to buy', err); + callback(null, data.id); + }.bind(this); + this.btcmakets.createOrder( + this.asset, + this.currency, + price, + amount, + 'Bid', + 'Limit', + id, + set + ); +} + +Trader.prototype.sell = function(amount, price, callback) { + price *= this.priceDivider; + amount = Math.floor(amount * this.priceDivider); + var id = Math.random() + '' + var set = function(err, data) { + if(!err && _.isEmpty(data)) + err = 'no data'; + else if(!err && !_.isEmpty(data.errorMessage)) + err = data.errorMessage; + if(err) + return log.error('unable to sell', err) + callback(null, data.id); + }.bind(this); + this.btcmakets.createOrder( + this.asset, + this.currency, + price, + amount, + 'Ask', + 'Limit', + id, + set + ); +} + +Trader.prototype.checkOrder = function(order, callback) { + var args = _.toArray(arguments); + + if (order == null) { + return callback(null, true); + } + + var check = function(err, data) { + if(!err && _.isEmpty(data.orders)) + err = 'no data'; + else if(!err && !_.isEmpty(data.errorMessage)) + err = data.errorMessage; + if(err){ + return log.error('unable to check order: ', order, '(', err ,'), retrying...'); + this.retry(this.checkOrder, args); + } + var placed = !_.isEmpty(data.orders) + callback(err, !placed); + }.bind(this); + + this.btcmakets.getOpenOrders(this.asset, this.currency, 10, null, check); +} + +Trader.prototype.getOrder = function(order, callback) { + var args = _.toArray(arguments); + var get = function(err, data) { + + if(!err && _.isEmpty(data.orders)) + err = 'no data'; + else if(!err && !_.isEmpty(data.errorMessage)) + err = data.errorMessage; + if(err){ + return log.error('unable to get order detail: ', order, '(', err ,'), retrying...'); + this.retry(this.getOrder, args); + } + var price = parseFloat(data.orders[0].price); + var amount = parseFloat(data.orders[0].volumn); + var date = moment.unix(data.orders[0].creationDate); + + callback(undefined, {price, amount, date}); + }.bind(this); + + this.btcmakets.getOrderDetail([order], callback); +} + +Trader.prototype.cancelOrder = function(order, callback) { + var args = _.toArray(arguments); + var get = function(err, data) { + + if(!err && _.isEmpty(data)) + err = 'no data'; + else if(!err && !_.isEmpty(data.errorMessage)) + err = data.errorMessage; + if(err){ + return log.error('unable to cancel order: ',order, '(', err, '), retrying...'); + this.retry(this.cancelOrder, args); + } + callback(); + }; + this.btcmakets.cancelOrders([order], callback); +} + +Trader.prototype.getTrades = function(since, callback, descending) { + var args = _.toArray(arguments); + var process = function(err, result) { + if(err) + return this.retry(this.getTrades, args); + + callback(null, result.reverse()); + }.bind(this); + + // supports `since` based on trade ID, Gekko can't work this atm.. + this.btcmakets.getTrades(this.asset, this.currency, process); +} + +Trader.getCapabilities = function () { + return { + name: 'BTC Markets', + slug: 'btc-markets', + currencies: ['AUD', 'BTC'], + assets: [ + 'BTC', 'LTC', 'ETH', 'ETC', 'BCH', 'XRP' + ], + markets: [ + { pair: ['AUD', 'BTC'], minimalOrder: { amount: 0.001, unit: 'asset' } }, + { pair: ['AUD', 'LTC'], minimalOrder: { amount: 0.001, unit: 'asset' } }, + { pair: ['AUD', 'ETH'], minimalOrder: { amount: 0.001, unit: 'asset' } }, + { pair: ['AUD', 'ETC'], minimalOrder: { amount: 0.001, unit: 'asset' } }, + { pair: ['AUD', 'BCH'], minimalOrder: { amount: 0.001, unit: 'asset' } }, + { pair: ['AUD', 'XRP'], minimalOrder: { amount: 0.001, unit: 'asset' } }, + { pair: ['BTC', 'LTC'], minimalOrder: { amount: 0.001, unit: 'asset' } }, + { pair: ['BTC', 'ETH'], minimalOrder: { amount: 0.001, unit: 'asset' } }, + { pair: ['BTC', 'ETC'], minimalOrder: { amount: 0.001, unit: 'asset' } }, + { pair: ['BTC', 'BCH'], minimalOrder: { amount: 0.001, unit: 'asset' } }, + { pair: ['BTC', 'XRP'], minimalOrder: { amount: 0.001, unit: 'asset' } } + ], + requires: ['key', 'secret'], + tid: 'tid', + providesHistory: 'scan', + providesFullHistory: false, + tradable: true + }; +} + +module.exports = Trader; diff --git a/exchange/wrappers/btcc.js.old b/exchange/wrappers/btcc.js.old new file mode 100644 index 0000000..0e0f868 --- /dev/null +++ b/exchange/wrappers/btcc.js.old @@ -0,0 +1,176 @@ +var BTCChina = require('btc-china-fork'); +var util = require('../core/util.js'); +var _ = require('lodash'); +var moment = require('moment'); +var log = require('../core/log'); + +var Trader = function(config) { + _.bindAll(this); + if(_.isObject(config)) { + this.key = config.key; + this.secret = config.secret; + this.clientID = config.username; + } + this.name = 'BTCC'; + + this.pair = (config.asset + config.currency).toUpperCase(); + + this.btcc = new BTCChina(this.key, this.secret, this.clientID); +} + +// if the exchange errors we try the same call again after +// waiting 10 seconds +Trader.prototype.retry = function(method, args) { + var wait = +moment.duration(10, 'seconds'); + log.debug(this.name, 'returned an error, retrying..'); + + var self = this; + + // make sure the callback (and any other fn) + // is bound to Trader + _.each(args, function(arg, i) { + if(_.isFunction(arg)) + args[i] = _.bind(arg, self); + }); + + // run the failed method again with the same + // arguments after wait + setTimeout( + function() { method.apply(self, args) }, + wait + ); +} + +Trader.prototype.getTicker = function(callback) { + var args = _.toArray(arguments); + var process = function(err, result) { + if(err) + return this.retry(this.getTicker, args); + + callback(null, { + bid: result.bids[0][0], + ask: result.asks[0][0] + }); + + }.bind(this); + + this.btcc.getOrderBook(process, this.pair, 1); +} + +Trader.prototype.getTrades = function(since, callback, descending) { + var args = _.toArray(arguments); + var process = function(err, result) { + if(err) + return this.retry(this.getTrades, args); + + if(descending) + callback(null, result.reverse()); + else + callback(null, result); + }.bind(this); + + + if(!since) + since = 500; + else + since = 5000; + + this.btcc.getHistoryData(process, {limit: since}); +} + +Trader.prototype.getPortfolio = function(callback) { + var args = _.toArray(arguments); + var set = function(err, data) { + if(err) + return this.retry(this.getPortfolio, args); + + var portfolio = []; + _.each(data.result.balance, function(obj) { + portfolio.push({name: obj.currency, amount: parseFloat(obj.amount)}); + }); + callback(err, portfolio); + }.bind(this); + + this.btcc.getAccountInfo(set, 'ALL'); +} + +Trader.prototype.getFee = function(callback) { + var args = _.toArray(arguments); + var set = function(err, data) { + if(err) + this.retry(this.getFee, args); + + callback(false, data.result.profile.trade_fee / 100); + }.bind(this); + + this.btcc.getAccountInfo(set, 'ALL'); +} + +Trader.prototype.buy = function(amount, price, callback) { + // TODO: do somewhere better.. + amount = Math.floor(amount * 10000) / 10000; + + var set = function(err, result) { + if(err) + return log.error('unable to buy:', err, result); + + callback(null, result.result); + }.bind(this); + + this.btcc.createOrder2(set, 'buy', price, amount, this.pair); +} + +Trader.prototype.sell = function(amount, price, callback) { + // TODO: do somewhere better.. + amount = Math.round(amount * 10000) / 10000; + + var set = function(err, result) { + if(err) + return log.error('unable to sell:', err, result); + + callback(null, result.result); + }.bind(this); + + this.btcc.createOrder2(set, 'sell', price, amount, this.pair); +} + +Trader.prototype.checkOrder = function(order, callback) { + var args = _.toArray(arguments); + var check = function(err, result) { + if(err) + this.retry(this.checkOrder, args); + + var done = result.result.order.status === 'closed'; + callback(err, done); + }; + + this.btcc.getOrder(check, order, this.pair, true); +} + +Trader.prototype.cancelOrder = function(order, callback) { + var cancel = function(err, result) { + if(err) + log.error('unable to cancel order', order, '(', err, result, ')'); + }.bind(this); + + this.btcc.cancelOrder(cancel, order, this.pair); +} + +Trader.getCapabilities = function () { + return { + name: 'BTCC', + slug: 'btcc', + currencies: ['BTC', 'CNY'], + assets: ['BTC', 'LTC'], + markets: [ + { pair: ['CNY', 'BTC'], minimalOrder: { amount: 0.001, unit: 'asset' } }, + { pair: ['CNY', 'LTC'], minimalOrder: { amount: 0.001, unit: 'asset' } }, + { pair: ['BTC', 'LTC'], minimalOrder: { amount: 0.001, unit: 'asset' } } + ], + requires: ['key', 'secret'], + tid: 'tid', + providesFullHistory: true, + }; +} + +module.exports = Trader; \ No newline at end of file diff --git a/exchange/wrappers/bx.in.th.js.old b/exchange/wrappers/bx.in.th.js.old new file mode 100644 index 0000000..851b7b3 --- /dev/null +++ b/exchange/wrappers/bx.in.th.js.old @@ -0,0 +1,89 @@ +var BitexthaiAPI = require('bitexthai'); +var util = require('../core/util.js'); +var _ = require('lodash'); +var moment = require('moment'); +var log = require('../core/log'); + +var Trader = function(config) { + _.bindAll(this); + if(_.isObject(config)) { + this.key = config.key; + this.secret = config.secret; + this.clientID = config.username; + } + this.name = 'BX.in.th'; + + this.pair = 1; // todo + + this.bitexthai = new BitexthaiAPI(this.key, this.secret, this.clientID); +} + +// if the exchange errors we try the same call again after +// waiting 10 seconds +Trader.prototype.retry = function(method, args) { + var wait = +moment.duration(10, 'seconds'); + log.debug(this.name, 'returned an error, retrying..'); + + var self = this; + + // make sure the callback (and any other fn) + // is bound to Trader + _.each(args, function(arg, i) { + if(_.isFunction(arg)) + args[i] = _.bind(arg, self); + }); + + // run the failed method again with the same + // arguments after wait + setTimeout( + function() { method.apply(self, args) }, + wait + ); +} + +Trader.prototype.getTrades = function(since, callback, descending) { + var args = _.toArray(arguments); + var process = function(err, result) { + if(err) + return this.retry(this.getTrades, args); + + var parsedTrades = []; + _.each(result.trades, function(trade) { + // We get trade time in local time, which is GMT+7 + var date = moment(trade.trade_date).subtract(7, 'hours').unix(); + + parsedTrades.push({ + date: date, + price: parseFloat(trade.rate), + amount: parseFloat(trade.amount), + tid: trade.trade_id + }); + }, this); + + if(descending) + callback(null, parsedTrades.reverse()); + else + callback(null, parsedTrades); + }.bind(this); + + this.bitexthai.trades(this.pair, process); +} + +Trader.getCapabilities = function () { + return { + name: 'BX.in.th', + slug: 'bx.in.th', + currencies: ['THB'], + assets: ['BTC'], + markets: [ + { + pair: ['THB', 'BTC'], minimalOrder: { amount: 0.0001, unit: 'asset' }, + } + ], + requires: ['key', 'secret'], + tradeError: 'NOT IMPLEMENTED YET', + providesHistory: false + }; +} + +module.exports = Trader; \ No newline at end of file diff --git a/exchange/wrappers/cexio.js.old b/exchange/wrappers/cexio.js.old new file mode 100644 index 0000000..2dafd00 --- /dev/null +++ b/exchange/wrappers/cexio.js.old @@ -0,0 +1,211 @@ +var CEXio = require('cexio'), + moment = require('moment'), + async = require('async'), + _ = require('lodash'), + util = require('../core/util'), + log = require('../core/log'); + +var Trader = function(config) { + this.user = config.username; + this.key = config.key; + this.secret = config.secret; + this.currency = config.currency.toUpperCase(); + this.asset = config.asset.toUpperCase(); + this.pair = this.asset + '_' + this.currency; + this.name = 'cex.io'; + + this.cexio = new CEXio( + this.pair, + this.user, + this.key, + this.secret + ); + + _.bindAll(this); +} + +Trader.prototype.getTrades = function(since, callback, descending) { + var args = _.toArray(arguments); + var process = function(err, trades) { + if(err || !trades || trades.length === 0) + return this.retry(this.getTrades, args, err); + + if(descending) + callback(null, trades); + else + callback(null, trades.reverse()); + } + + this.cexio.trades({}, _.bind(process, this)); +} + +Trader.prototype.buy = function(amount, price, callback) { + // Prevent "You incorrectly entered one of fields." + // because of more than 8 decimals. + amount *= 100000000; + amount = Math.floor(amount); + amount /= 100000000; + + log.debug('BUY', amount, this.asset, '@', price, this.currency); + + var set = function(err, data) { + if(err) + return log.error('unable to buy:', err); + if(data.error) + return log.error('unable to buy:', data.error); + + log.debug('BUY order placed. Order ID', data.id); + callback(null, data.id); + }; + + this.cexio.place_order('buy', amount, price, _.bind(set, this)); +} + +Trader.prototype.sell = function(amount, price, callback) { + // Prevent "You incorrectly entered one of fields." + // because of more than 8 decimals. + amount *= 100000000; + amount = Math.floor(amount); + amount /= 100000000; + + // test placing orders which will not be filled + //price *= 10; price = price.toFixed(8); + + log.debug('SELL', amount, this.asset, '@', price, this.currency); + + var set = function(err, data) { + if(err) + return log.error('unable to sell:', err); + if(data.error) + return log.error('unable to sell:', data.error); + + log.debug('SELL order placed. Order ID', data.id); + callback(null, data.id); + }; + + this.cexio.place_order('sell', amount, price, _.bind(set, this)); +} + +Trader.prototype.retry = function(method, args, err) { + var wait = +moment.duration(10, 'seconds'); + log.debug(this.name, 'returned an error, retrying..', err, 'waiting for', wait, 'ms'); + + if (!_.isFunction(method)) { + log.error(this.name, 'failed to retry, no method supplied.'); + return; + } + + var self = this; + + // make sure the callback (and any other fn) + // is bound to Trader + _.each(args, function(arg, i) { + if(_.isFunction(arg)) + args[i] = _.bind(arg, self); + }); + + // run the failed method again with the same + // arguments after wait + setTimeout( + function() { method.apply(self, args) }, + wait + ); +} + +Trader.prototype.getPortfolio = function(callback) { + var args = _.toArray(arguments); + var calculate = function(err, data) { + if(err) + return this.retry(this.getPortfolio, args, err); + + currency = parseFloat(data[this.currency].available) + if(parseFloat(data[this.currency].orders)){ + currency -= parseFloat(data[this.currency].orders) + } + assets = parseFloat(data[this.asset].available); + if( parseFloat(data[this.asset].orders)){ + assets -= parseFloat(data[this.asset].orders); + } + + var portfolio = []; + portfolio.push({name: this.currency, amount: currency}); + portfolio.push({name: this.asset, amount: assets}); + callback(err, portfolio); + } + this.cexio.balance(calculate.bind(this)); +} + +Trader.prototype.getTicker = function(callback) { + var set = function(err, data) { + var ticker = { + ask: data.ask, + bid: data.bid + }; + callback(err, ticker); + } + this.cexio.ticker(_.bind(set, this)); +} + +Trader.prototype.getFee = function(callback) { + // cexio does currently don't take a fee on trades + // TODO: isn't there an API call for this? + callback(false, 0.002); +} + +Trader.prototype.checkOrder = function(order, callback) { + var check = function(err, result) { + + if(err) + return callback(false, true); + if(result.error) + return callback(false, true); + + var exists = false; + _.each(result, function(entry) { + if(entry.id === order) { + exists = true; + return; + } + }); + callback(err, !exists); + }; + + this.cexio.open_orders(_.bind(check, this)); +} + +Trader.prototype.cancelOrder = function(order) { + var check= function(err, result) { + if(err) + log.error('cancel order failed:', err); + if(typeof(result) !== 'undefined' && result.error) + log.error('cancel order failed:', result.error); + } + this.cexio.cancel_order(order, check); +} + +Trader.getCapabilities = function () { + return { + name: 'CEX.io', + slug: 'cexio', + currencies: ['BTC', 'USD', 'EUR', 'RUB'], + assets: ['GHS', 'BTC', 'ETH', 'LTC'], + markets: [ + { pair: ['BTC', 'GHS'], minimalOrder: { amount: 0.000001, unit: 'currency' } }, + { pair: ['BTC', 'LTC'], minimalOrder: { amount: 0.000001, unit: 'currency' } }, + { pair: ['EUR', 'LTC'], minimalOrder: { amount: 0.000001, unit: 'currency' } }, + { pair: ['USD', 'LTC'], minimalOrder: { amount: 0.000001, unit: 'currency' } }, + { pair: ['RUB', 'BTC'], minimalOrder: { amount: 0.000001, unit: 'currency' } }, + { pair: ['USD', 'BTC'], minimalOrder: { amount: 0.000001, unit: 'currency' } }, + { pair: ['EUR', 'BTC'], minimalOrder: { amount: 0.000001, unit: 'currency' } }, + { pair: ['BTC', 'ETH'], minimalOrder: { amount: 0.000001, unit: 'currency' } }, + { pair: ['USD', 'ETH'], minimalOrder: { amount: 0.000001, unit: 'currency' } }, + { pair: ['EUR', 'ETH'], minimalOrder: { amount: 0.000001, unit: 'currency' } } + ], + requires: ['key', 'secret', 'username'], + providesHistory: false, + tid: 'tid' + }; +} + +module.exports = Trader; + diff --git a/exchange/wrappers/coinbase-markets.json b/exchange/wrappers/coinbase-markets.json new file mode 100644 index 0000000..57885cb --- /dev/null +++ b/exchange/wrappers/coinbase-markets.json @@ -0,0 +1,238 @@ +{ + "assets": [ + "ETC", + "BTC", + "BCH", + "ETH", + "LTC", + "ZRX" + ], + "currencies": [ + "EUR", + "USD", + "BTC", + "GBP" + ], + "markets": [ + { + "pair": [ + "EUR", + "ETC" + ], + "minimalOrder": { + "amount": "0.10000000", + "unit": "asset" + } + }, + { + "pair": [ + "USD", + "BTC" + ], + "minimalOrder": { + "amount": "0.00100000", + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "BCH" + ], + "minimalOrder": { + "amount": "0.01000000", + "unit": "asset" + } + }, + { + "pair": [ + "USD", + "BCH" + ], + "minimalOrder": { + "amount": "0.01000000", + "unit": "asset" + } + }, + { + "pair": [ + "EUR", + "BTC" + ], + "minimalOrder": { + "amount": "0.00100000", + "unit": "asset" + } + }, + { + "pair": [ + "GBP", + "BTC" + ], + "minimalOrder": { + "amount": "0.00100000", + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "ETH" + ], + "minimalOrder": { + "amount": "0.01000000", + "unit": "asset" + } + }, + { + "pair": [ + "EUR", + "ETH" + ], + "minimalOrder": { + "amount": "0.01000000", + "unit": "asset" + } + }, + { + "pair": [ + "USD", + "ETH" + ], + "minimalOrder": { + "amount": "0.01000000", + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "LTC" + ], + "minimalOrder": { + "amount": "0.10000000", + "unit": "asset" + } + }, + { + "pair": [ + "EUR", + "LTC" + ], + "minimalOrder": { + "amount": "0.10000000", + "unit": "asset" + } + }, + { + "pair": [ + "USD", + "LTC" + ], + "minimalOrder": { + "amount": "0.10000000", + "unit": "asset" + } + }, + { + "pair": [ + "EUR", + "BCH" + ], + "minimalOrder": { + "amount": "0.01000000", + "unit": "asset" + } + }, + { + "pair": [ + "USD", + "ETC" + ], + "minimalOrder": { + "amount": "0.10000000", + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "ETC" + ], + "minimalOrder": { + "amount": "0.10000000", + "unit": "asset" + } + }, + { + "pair": [ + "USD", + "ZRX" + ], + "minimalOrder": { + "amount": "1.00000000", + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "ZRX" + ], + "minimalOrder": { + "amount": "1.00000000", + "unit": "asset" + } + }, + { + "pair": [ + "EUR", + "ZRX" + ], + "minimalOrder": { + "amount": "1.00000000", + "unit": "asset" + } + }, + { + "pair": [ + "GBP", + "ETC" + ], + "minimalOrder": { + "amount": "0.10000000", + "unit": "asset" + } + }, + { + "pair": [ + "GBP", + "ETH" + ], + "minimalOrder": { + "amount": "0.01000000", + "unit": "asset" + } + }, + { + "pair": [ + "GBP", + "LTC" + ], + "minimalOrder": { + "amount": "0.10000000", + "unit": "asset" + } + }, + { + "pair": [ + "GBP", + "BCH" + ], + "minimalOrder": { + "amount": "0.01000000", + "unit": "asset" + } + } + ] +} \ No newline at end of file diff --git a/exchange/wrappers/coinfalcon-markets.json b/exchange/wrappers/coinfalcon-markets.json new file mode 100644 index 0000000..b277966 --- /dev/null +++ b/exchange/wrappers/coinfalcon-markets.json @@ -0,0 +1,316 @@ +{ + "assets": [ + "IOT", + "ETH", + "LTC", + "BTC", + "NANO", + "BCH", + "KIN", + "TRX", + "CRPT", + "GRLC", + "ECA", + "XRP", + "ADA", + "DOGE", + "STQ", + "DASC", + "EOS", + "DTX" + ], + "currencies": [ + "BTC", + "EUR", + "ETH", + "USDT" + ], + "markets": [ + { + "pair": [ + "BTC", + "IOT" + ], + "minimalOrder": { + "amount": 1e-8, + "price": 1e-8, + "order": 0 + } + }, + { + "pair": [ + "BTC", + "ETH" + ], + "minimalOrder": { + "amount": 1e-8, + "price": 0.00001, + "order": 0 + } + }, + { + "pair": [ + "BTC", + "LTC" + ], + "minimalOrder": { + "amount": 1e-8, + "price": 0.00001, + "order": 0 + } + }, + { + "pair": [ + "EUR", + "BTC" + ], + "minimalOrder": { + "amount": 1e-8, + "price": 0.01, + "order": 0 + } + }, + { + "pair": [ + "BTC", + "NANO" + ], + "minimalOrder": { + "amount": 1e-8, + "price": 1e-8, + "order": 0 + } + }, + { + "pair": [ + "BTC", + "BCH" + ], + "minimalOrder": { + "amount": 1e-8, + "price": 0.00001, + "order": 0 + } + }, + { + "pair": [ + "BTC", + "KIN" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-10, + "order": 0 + } + }, + { + "pair": [ + "ETH", + "KIN" + ], + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0 + } + }, + { + "pair": [ + "BTC", + "TRX" + ], + "minimalOrder": { + "amount": 1e-8, + "price": 1e-8, + "order": 0 + } + }, + { + "pair": [ + "BTC", + "CRPT" + ], + "minimalOrder": { + "amount": 1e-8, + "price": 1e-8, + "order": 0 + } + }, + { + "pair": [ + "BTC", + "GRLC" + ], + "minimalOrder": { + "amount": 1e-8, + "price": 1e-8, + "order": 0 + } + }, + { + "pair": [ + "BTC", + "ECA" + ], + "minimalOrder": { + "amount": 1e-8, + "price": 1e-8, + "order": 0 + } + }, + { + "pair": [ + "ETH", + "ECA" + ], + "minimalOrder": { + "amount": 1e-8, + "price": 1e-8, + "order": 0 + } + }, + { + "pair": [ + "BTC", + "XRP" + ], + "minimalOrder": { + "amount": 1e-8, + "price": 1e-8, + "order": 0 + } + }, + { + "pair": [ + "BTC", + "ADA" + ], + "minimalOrder": { + "amount": 0.000001, + "price": 1e-8, + "order": 0 + } + }, + { + "pair": [ + "ETH", + "ADA" + ], + "minimalOrder": { + "amount": 0.000001, + "price": 1e-8, + "order": 0 + } + }, + { + "pair": [ + "BTC", + "DOGE" + ], + "minimalOrder": { + "amount": 0.00001, + "price": 1e-8, + "order": 0 + } + }, + { + "pair": [ + "BTC", + "STQ" + ], + "minimalOrder": { + "amount": 0.01, + "price": 1e-8, + "order": 0 + } + }, + { + "pair": [ + "EUR", + "IOT" + ], + "minimalOrder": { + "amount": 0.00001, + "price": 0.0001, + "order": 0 + } + }, + { + "pair": [ + "BTC", + "DASC" + ], + "minimalOrder": { + "amount": 0.00001, + "price": 1e-8, + "order": 0 + } + }, + { + "pair": [ + "ETH", + "DASC" + ], + "minimalOrder": { + "amount": 0.00001, + "price": 1e-8, + "order": 0 + } + }, + { + "pair": [ + "EUR", + "DASC" + ], + "minimalOrder": { + "amount": 0.00001, + "price": 0.0001, + "order": 0 + } + }, + { + "pair": [ + "EUR", + "NANO" + ], + "minimalOrder": { + "amount": 0.0001, + "price": 0.0001, + "order": 0 + } + }, + { + "pair": [ + "USDT", + "BTC" + ], + "minimalOrder": { + "amount": 1e-8, + "price": 0.01, + "order": 0 + } + }, + { + "pair": [ + "USDT", + "EOS" + ], + "minimalOrder": { + "amount": 0.0001, + "price": 0.01, + "order": 0 + } + }, + { + "pair": [ + "BTC", + "DTX" + ], + "minimalOrder": { + "amount": 0.01, + "price": 1e-8, + "order": 0 + } + } + ] +} \ No newline at end of file diff --git a/exchange/wrappers/coinfalcon.js b/exchange/wrappers/coinfalcon.js new file mode 100644 index 0000000..c5fc424 --- /dev/null +++ b/exchange/wrappers/coinfalcon.js @@ -0,0 +1,385 @@ +const moment = require('moment'); +const _ = require('lodash'); +const marketData = require('./coinfalcon-markets.json'); + +const CoinFalcon = require('coinfalcon'); + +var Trader = function(config) { + _.bindAll(this, [ + 'roundAmount', + 'roundPrice' + ]); + + this.makerFee = 0; + + if (_.isObject(config)) { + this.key = config.key; + this.secret = config.secret; + this.currency = config.currency.toUpperCase(); + this.asset = config.asset.toUpperCase(); + } + + this.pair = this.asset + '-' + this.currency; + this.name = 'coinfalcon'; + + this.market = _.find(Trader.getCapabilities().markets, (market) => { + return market.pair[0] === this.currency && market.pair[1] === this.asset + }); + + this.coinfalcon = new CoinFalcon.Client(this.key, this.secret); + + this.interval = 1500; +}; + +const includes = (str, list) => { + if(!_.isString(str)) + return false; + + return _.some(list, item => str.includes(item)); +} + +const recoverableErrors = [ + 'SOCKETTIMEDOUT', + 'TIMEDOUT', + 'CONNRESET', + 'ECONNRESET', + 'CONNREFUSED', + 'NOTFOUND', + '429', + '522', + '429', + '504', + '503', + '500', + '502', + '408', + // "The timestamp 1527996378 is invalid, current timestamp is 1527996441." + 'is invalid, current timestamp is', + 'EHOSTUNREACH', + // https://github.com/askmike/gekko/issues/2407 + 'We are fixing a few issues, be back shortly.', + 'Client network socket disconnected before secure TLS connection was established', + 'socket hang up', + // getaddrinfo EAI_AGAIN coinfalcon.com coinfalcon.com:443 + 'EAI_AGAIN' +]; + +// errors that might mean +// the API call succeeded. +const unknownResultErrors = [ + '524', +] + +Trader.prototype.processResponse = function(method, args, next) { + + const requestAt = moment(); + + const checkTime = () => { + const diff = moment().diff(requestAt, 's'); + if(diff > 10) { + console.log(new Date, '[CF] API CALL TOOK', diff, 'SECONDS!'); + console.log(new Date, '[CF]', {method, args, next}); + } + } + + const catcher = err => { + if(!err || !err.message) { + err = new Error(err || 'Empty error'); + } + + if(includes(err.message, recoverableErrors)) { + return this.retry(method, args); + } + + console.log(new Date, '[cf] big error!', err.message, method); + + return next(err); + } + + return { + failure: catcher, + success: data => { + if(!data) + return catcher(); + + if(data.error) + return catcher(data.error); + + if(includes(data, ['Please complete the security check to proceed.'])) + return next(new Error( + 'Your IP has been flagged by CloudFlare. ' + + 'As such Gekko Broker cannot access Coinfalcon.' + )); + + next(undefined, data); + } + } +} + +Trader.prototype.retry = function(method, args) { + var wait = +moment.duration(1, 'seconds'); + + // run the failed method again with the same arguments after wait + setTimeout(() => { + method.apply(this, args); + }, wait); +}; + +Trader.prototype.getTicker = function(callback) { + const handle = this.processResponse(this.getTicker, [callback], (err, res) => { + if(err) + return callback(err); + + callback(null, {bid: +res.data.bids[0].price, ask: +res.data.asks[0].price}) + }); + + var url = "markets/" + this.pair + "/orders?level=1" + + this.coinfalcon.get(url).then(handle.success).catch(handle.failure); +}; + +Trader.prototype.getFee = function(callback) { + callback(false, this.makerFee / 100); +}; + +Trader.prototype.getPortfolio = function(callback) { + const handle = this.processResponse(this.getPortfolio, [callback], (err, res) => { + if(err) + return callback(err); + + var portfolio = res.data.map(account => ({ + name: account.currency_code.toUpperCase(), + amount: parseFloat(account.available_balance) + })); + + callback(null, portfolio); + }); + + this.coinfalcon.get('user/accounts').then(handle.success).catch(handle.failure); +}; + +Trader.prototype.getOpenOrders = function(callback) { + const handle = this.processResponse(this.getOpenOrders, [callback], (err, res) => { + if(err) + return callback(err); + + console.log(new Date, 'CF getOpenOrders', res.data); + + callback(null, res.data.map(o => o.id)); + }); + + this.coinfalcon.get('user/orders').then(handle.success).catch(handle.failure); +}; + +Trader.prototype.addOrder = function(type, amount, price, callback) { + const args = _.toArray(arguments); + + const handle = this.processResponse(this.addOrder, args, (err, res) => { + if(err) + return callback(err); + + if(!res.data.id) { + console.log(new Date, 'CF ERROR! CREATED ORDER BUT NO ID', res); + } + + callback(false, res.data.id); + }); + + const payload = { + order_type: type, + operation_type: 'limit_order', + market: this.pair, + size: amount + '', + price: price + '' + } + + this.coinfalcon.post('user/orders', payload).then(handle.success).catch(handle.failure); +}; + +['buy', 'sell'].map(function(type) { + Trader.prototype[type] = function(amount, price, callback) { + this.addOrder(type, amount, price, callback); + }; +}); + +const round = function(number, precision) { + var factor = Math.pow(10, precision); + var tempNumber = number * factor; + var roundedTempNumber = Math.round(tempNumber); + return roundedTempNumber / factor; +}; + +// ticksize 0.01 will yield: 2 +Trader.prototype.getPrecision = function(tickSize) { + if (!isFinite(tickSize)) { + return 0; + } + var e = 1; + var p = 0; + while (Math.round(tickSize * e) / e !== tickSize) { + e *= 10; p++; + } + return p; +}; + +Trader.prototype.roundAmount = function(amount) { + return round(amount, this.getPrecision(this.market.minimalOrder.amount)); +} + +Trader.prototype.roundPrice = function(price) { + return round(price, this.getPrecision(this.market.minimalOrder.price)); +} + +Trader.prototype.outbidPrice = function(price, isUp) { + let newPrice; + + if(isUp) { + newPrice = price + this.market.minimalOrder.price; + } else { + newPrice = price - this.market.minimalOrder.price; + } + + return this.roundPrice(newPrice); +} + +Trader.prototype.getOrder = function(order, callback) { + const args = _.toArray(arguments); + const handle = this.processResponse(this.getOrder, args, (err, res) => { + if(err) + return callback(err); + + const price = parseFloat(res.data.price); + const amount = parseFloat(res.data.size_filled); + const date = moment(res.data.created_at); + const fees = {}; + const feePercent = this.makerFee; + callback(false, { price, amount, date, fees, feePercent }); + }); + + this.coinfalcon.get('user/orders/' + order).then(handle.success).catch(handle.failure); +}; + +Trader.prototype.checkOrder = function(order, callback) { + const args = _.toArray(arguments); + + const handle = this.processResponse(this.checkOrder, args, (err, res) => { + if(err) + return callback(err); + + // https://docs.coinfalcon.com/#list-orders + const status = res.data.status; + + if(status === 'canceled') { + return callback(undefined, { executed: false, open: false }); + } if(status === 'fulfilled') { + return callback(undefined, { executed: true, open: false }); + } if( + status === 'pending' || + status === 'partially_filled' || + status === 'open' + ) { + return callback(undefined, { executed: false, open: true, filledAmount: +res.data.size_filled }); + } + + console.error(res.data); + callback(new Error('Unknown status ' + status)); + }); + + this.coinfalcon.get('user/orders/' + order).then(handle.success).catch(handle.failure); +}; + +Trader.prototype.cancelOrder = function(order, callback) { + const args = _.toArray(arguments); + + const handle = this.processResponse(this.cancelOrder, args, (err, res) => { + + if(err) { + if(err.message.includes('has wrong status.')) { + + // see https://github.com/askmike/gekko/issues/2440 + return setTimeout(() => { + this.checkOrder(order, (err, 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); + } + + callback(undefined, false, { + filled: +res.data.size_filled + }); + }); + + this.coinfalcon.delete('user/orders/' + order).then(handle.success).catch(handle.failure); +}; + +Trader.prototype.getTrades = function(since, callback, descending) { + var args = _.toArray(arguments); + + var success = function(res) { + var parsedTrades = []; + _.each( + res.data, + function(trade) { + parsedTrades.push({ + tid: trade.id, + date: moment(trade.created_at).unix(), + price: parseFloat(trade.price), + amount: parseFloat(trade.size), + }); + }, + this + ); + + if (descending) { + callback(null, parsedTrades); + } else { + callback(null, parsedTrades.reverse()); + } + }.bind(this); + + var failure = function (err) { + err = new Error(err); + return this.retry(this.getTrades, args, err); + }.bind(this); + + var url = "markets/" + this.pair + "/trades" + + if (since) { + url += '?since_time=' + (_.isString(since) ? since : since.format()); + } + + this.coinfalcon.get(url).then(success).catch(failure); +}; + +Trader.getCapabilities = function () { + return { + name: 'CoinFalcon', + slug: 'coinfalcon', + assets: marketData.assets, + currencies: marketData.currencies, + markets: marketData.markets, + requires: ['key', 'secret'], + providesHistory: 'date', + providesFullHistory: true, + tid: 'tid', + tradable: true, + forceReorderDelay: false, + gekkoBroker: 0.6 + }; +} + +module.exports = Trader; diff --git a/exchange/wrappers/coingi.js b/exchange/wrappers/coingi.js new file mode 100755 index 0000000..8ebd062 --- /dev/null +++ b/exchange/wrappers/coingi.js @@ -0,0 +1,283 @@ +var Coingi = require('coingi'); +var moment = require('moment'); +var _ = require('lodash'); + +var util = require('../core/util'); +var Errors = require('../core/error'); +var log = require('../core/log'); + + +var Trader = function (config) { + _.bindAll(this); + + if (_.isObject(config)) { + this.key = config.key; + this.secret = config.secret; + this.currency = config.currency.toUpperCase() + this.asset = config.asset.toUpperCase(); + } + + this.pair = this.asset + "-" + this.currency; + this.name = 'coingi'; + this.since = null; + + this.coingi = new Coingi( + this.key, + this.secret, + {timeout: +moment.duration(60, 'seconds')} + ); +} + +var retryCritical = { + retries: 10, + factor: 1.2, + minTimeout: 1 * 1000, + maxTimeout: 30 * 1000 +}; + +var retryForever = { + forever: true, + factor: 1.2, + minTimeout: 10, + maxTimeout: 30 +}; + +var recoverableErrors = new RegExp(/(SOCKETTIMEDOUT|TIMEDOUT|CONNRESET|CONNREFUSED|NOTFOUND|API:Invalid nonce|Service:Unavailable|Request timed out|Response code 520|Response code 504|Response code 502)/) + +Trader.prototype.processError = function (funcName, error) { + if (!error) + return undefined; + + if (!error.message.match(recoverableErrors)) { + log.error(`[coingi.js] (${funcName}) returned an irrecoverable error: ${error.message}`); + return new Errors.AbortError('[coingi.js] ' + error.message); + } + + log.debug(`[coingi.js] (${funcName}) returned an error, retrying: ${error.message}`); + return new Errors.RetryError('[coingi.js] ' + error.message); +}; + +Trader.prototype.handleResponse = function (funcName, callback) { + return (error, body) => { + if (!error) { + if (_.isEmpty(body)) + error = new Error('NO DATA WAS RETURNED'); + + else if (!_.isEmpty(body.error)) + error = new Error(body.error); + } + + return callback(this.processError(funcName, error), body); + } +}; + + + + +/* PORTFOLIO MANAGER */ + +Trader.prototype.getTicker = function (callback) { + var setTicker = function (err, data) { + if (err) + return callback(err); + + var ticker = { + ask: data.asks[0].price, + bid: data.bids[0].price + }; + callback(undefined, ticker); + }; + + let handler = (cb) => this.coingi.api('order-book', "/" + this.pair + "/1/1/1", this.handleResponse('getTicker', cb)); + util.retryCustom(retryForever, _.bind(handler, this), _.bind(setTicker, this)); +}; + + +Trader.prototype.getTrades = function (since, callback, ascending) { + var startTs = since ? moment(since).valueOf() : null; + + var processResults = function (err, trades) { + if (err) { + return callback(err); + } + + var parsedTrades = []; + _.each(trades, function (trade) { + // Even when you supply 'since' you can still get more trades than you asked for, it needs to be filtered + if (_.isNull(startTs) || startTs < moment(trade.timestamp).valueOf()) { + parsedTrades.push({ + type: trade.type === 0 ? "sell" : "buy", + date: moment(trade.timestamp).unix(), + amount: String(trade.amount), + price: String(trade.price), + tid: trade.timestamp + }); + } + }, this); + + if(ascending) + callback(undefined, parsedTrades); + else + callback(undefined, parsedTrades.reverse()); + }; + + var optionalSince = ""; + if (since) { + optionalSince = "/" + startTs; + } + + let handler = (cb) => this.coingi.api('transactions', "/" + this.pair + "/512" + optionalSince, this.handleResponse('getTrades', cb)); + util.retryCustom(retryForever, _.bind(handler, this), _.bind(processResults, this)); +}; + +Trader.prototype.getPortfolio = function (callback) { + var setBalance = function (err, data) { + if (err) + return callback(err); + + log.debug('[coingi.js] entering "setBalance" callback after coingi-api call, data:', data); + + const portfolio = []; + for (var i = 0; i < data.length; i++) { + portfolio.push({ + name: data[i].currency.name.toUpperCase(), + amount: data[i].available + }); + } + + return callback(undefined, portfolio); + }; + + let handler = (cb) => this.coingi.api('balance', {currencies: this.asset + "," + this.currency}, this.handleResponse('getPortfolio', cb)); + util.retryCustom(retryForever, _.bind(handler, this), _.bind(setBalance, this)); +}; + +// Base fee is 0.2% +Trader.prototype.getFee = function (callback) { + const fee = 0.2; + callback(undefined, fee / 100); +}; + +Trader.prototype.addOrder = function (tradeType, amount, price, callback) { + log.debug('[coingi.js] (add-order)', tradeType.toUpperCase(), amount, this.asset, '@', price, this.currency); + + var setOrder = function (err, data) { + if (err) + return callback(err); + + var uuid = data.result; + log.debug('[coingi.js] (addOrder) added order with uuid:', uuid); + + callback(undefined, uuid); + }; + + let reqData = { + currencyPair: this.pair, + type: tradeType.toLowerCase() === "sell" ? 0 : 1, + price: price, + volume: amount.toString() + }; + + let handler = (cb) => this.coingi.api('add-order', reqData, this.handleResponse('addOrder', cb)); + util.retryCustom(retryCritical, _.bind(handler, this), _.bind(setOrder, this)); +}; + + +Trader.prototype.getOrder = function (orderId, callback) { + var getOrder = function (err, order) { + if (err) + return callback(err); + + if (order !== null) { + const price = parseFloat(order.price); + const amount = parseFloat(order.baseAmount); + const date = moment.unix(order.timestamp); + callback(undefined, {price, amount, date}); + } else { + log.error("Error! Order ID '" + orderId + "' couldn't be found in the result of get order!"); + } + }; + + let reqData = {ordeId: orderId}; + let handler = (cb) => this.coingi.api('get-order', reqData, this.handleResponse('getOrder', cb)); + util.retryCustom(retryCritical, _.bind(handler, this), _.bind(getOrder, this)); +} + +Trader.prototype.buy = function (amount, price, callback) { + this.addOrder('buy', amount, price, callback); +}; + +Trader.prototype.sell = function (amount, price, callback) { + this.addOrder('sell', amount, price, callback); +}; + +Trader.prototype.checkOrder = function (orderId, callback) { + var check = function (err, order) { + if (err) + return callback(err); + + if (order === null) { + log.error("Error! Order ID '" + orderId + "' couldn't be found in the result of get order!"); + } + // returns true when the order has been filled (processed in full), false otherwise + callback(undefined, order !== null && order.status === 2); + }; + + let reqData = {orderId: orderId}; + let handler = (cb) => this.coingi.api('get-order', reqData, this.handleResponse('checkOrder', cb)); + util.retryCustom(retryCritical, _.bind(handler, this), _.bind(check, this)); +}; + +Trader.prototype.cancelOrder = function (order, callback) { + let reqData = {orderId: order}; + let handler = (cb) => this.coingi.api('cancel-order', reqData, this.handleResponse('cancelOrder', cb)); + util.retryCustom(retryForever, _.bind(handler, this), callback); +}; + +Trader.getCapabilities = function () { + return { + name: 'Coingi', + slug: 'coingi', + currencies: ['EUR', 'USD', 'BTC'], + assets: ['BTC', 'DASH', 'DOGE', 'EUR', 'LTC', 'NMC', 'PPC', 'VTC'], + markets: [ + // Tradeable against BTC + {pair: ['USD', 'BTC'], minimalOrder: {amount: 0.00001, unit: 'asset'}, precision: 8}, + {pair: ['EUR', 'BTC'], minimalOrder: {amount: 0.00001, unit: 'asset'}, precision: 8}, + + // Tradeable against DASH + {pair: ['BTC', 'DASH'], minimalOrder: {amount: 0.00001, unit: 'asset'}, precision: 8}, + + // Tradeable against DOGE + {pair: ['BTC', 'DOGE'], minimalOrder: {amount: 0.00001, unit: 'asset'}, precision: 8}, + {pair: ['USD', 'DOGE'], minimalOrder: {amount: 0.00001, unit: 'asset'}, precision: 8}, + + // Tradeable against EUR + {pair: ['USD', 'EUR'], minimalOrder: {amount: 0.01, unit: 'asset'}, precision: 2}, + + // Tradeable against LTC + {pair: ['BTC', 'LTC'], minimalOrder: {amount: 0.00001, unit: 'asset'}, precision: 8}, + {pair: ['EUR', 'LTC'], minimalOrder: {amount: 0.00001, unit: 'asset'}, precision: 8}, + {pair: ['USD', 'LTC'], minimalOrder: {amount: 0.00001, unit: 'asset'}, precision: 8}, + + // Tradeable against NMC + {pair: ['BTC', 'NMC'], minimalOrder: {amount: 0.00001, unit: 'asset'}, precision: 8}, + + // Tradeable against PPC + {pair: ['USD', 'PPC'], minimalOrder: {amount: 0.00001, unit: 'asset'}, precision: 8}, + {pair: ['EUR', 'PPC'], minimalOrder: {amount: 0.00001, unit: 'asset'}, precision: 8}, + {pair: ['BTC', 'PPC'], minimalOrder: {amount: 0.00001, unit: 'asset'}, precision: 8}, + + // Tradeable against VTC + {pair: ['BTC', 'VTC'], minimalOrder: {amount: 0.00001, unit: 'asset'}, precision: 8} + ], + requires: ['key', 'secret'], + providesHistory: 'date', + providesFullHistory: true, + exchangeMaxHistoryAge: 30, + tid: 'date', + tradable: true + }; +} + +module.exports = Trader; diff --git a/exchange/wrappers/exmo-markets.json b/exchange/wrappers/exmo-markets.json new file mode 100644 index 0000000..878cf2b --- /dev/null +++ b/exchange/wrappers/exmo-markets.json @@ -0,0 +1,43 @@ +{ + "assets": [ + "BTC", + "ETH" + ], + "currencies": [ + "EUR", + "USD", + "LTC" + ], + "markets": [ + { + "pair": [ + "USD", + "BTC" + ], + "minimalOrder": { + "amount": "0.001", + "unit": "asset" + } + }, + { + "pair": [ + "EUR", + "BTC" + ], + "minimalOrder": { + "amount": "0.001", + "unit": "asset" + } + }, + { + "pair": [ + "LTC", + "ETH" + ], + "minimalOrder": { + "amount": "0.01", + "unit": "asset" + } + } + ] +} diff --git a/exchange/wrappers/exmo.js b/exchange/wrappers/exmo.js new file mode 100644 index 0000000..200646a --- /dev/null +++ b/exchange/wrappers/exmo.js @@ -0,0 +1,239 @@ +const _ = require('lodash'); +const moment = require('moment'); +const retry = require('../exchangeUtils').retry; + + +const CryptoJS = require("crypto-js"); +const querystring = require('querystring'); +const request = require('request'); + +API_URL='https://api.exmo.com/v1/'; + +const marketData = require('./exmo-markets.json'); + + +const Trader = function(config) { + _.bindAll(this); + this.key=""; + this.secret=""; + + if(_.isObject(config)) { + if(_.isString(config.key)) this.key = config.key; + if(_.isString(config.secret)) this.secret = config.secret; + this.currency = config.currency; + this.asset = config.asset; + this.pair = this.asset + '_' + this.currency; + }; + + this.name = 'EXMO'; + this.nonce = new Date() * 1000; +} + + +const recoverableErrors = [ + 'SOCKETTIMEDOUT', + 'TIMEDOUT', + 'CONNRESET', + 'CONNREFUSED', + 'NOTFOUND', + 'EHOSTUNREACH', +]; + +const includes = (str, list) => { + if(!_.isString(str)) + return false; + + return _.some(list, item => str.includes(item)); +} + + +Trader.prototype.api_query = function(method, params, callback){ + params.nonce = this.nonce++; + var post_data = querystring.stringify(params); + + var options = { + url: API_URL + method, + headers: {'Key': this.key,'Sign': CryptoJS.HmacSHA512(post_data, this.secret).toString(CryptoJS.enc.hex) }, + form: params + }; + + request.post(options, function (error, response, body) { + if (!error && response.statusCode == 200) { + data=JSON.parse(body); + if(data.error) error = { message: data.error } + else if (data.result!=undefined && data.result==false) error = { message: '"result": false' } ; + callback(error, data); + } else { + console.log('cb request error'); + console.log(body); + + if(error) { + if(includes(error.message, recoverableErrors)) { + error.notFatal = true; + } + console.log(error); + callback(error); + }; + }; + }); +}; + +Trader.prototype.getTrades = function(since, callback, descending) { + const processResponse = (error, data) => { + if (error) return callback(error); + + data=data[this.pair]; + + var parsedTrades = _.map(data, function(trade) { + return { + tid: trade.trade_id, + date: trade.date, + price: +trade.price, + amount: +trade.quantity + }; + }); + + if (descending) callback(undefined, parsedTrades); + else callback(undefined, parsedTrades.reverse()); + }; + + const fetch = cb => this.api_query("trades", { pair: this.pair} , cb); + retry(null, fetch, processResponse); + +} + +Trader.prototype.getTicker = function(callback) { + const processResponse = (err, data) => { + if (err) + return callback(err); + + data=data[this.pair]; + + callback(undefined, {bid: +data.sell_price, ask: +data.buy_price}); + }; + + const fetch = cb => this.api_query("ticker", { pair: this.pair} , cb); + retry(null, fetch, processResponse); +} + +Trader.prototype.getFee = function(callback) { + callback(undefined, 0.002); +} + +Trader.prototype.roundAmount = function(amount) { + return _.floor(amount, 8); +}; + +Trader.prototype.roundPrice = function(price) { + return price; +} + +Trader.prototype.submitOrder = function(type, amount, price, callback) { + const processResponse = (err, data) => { + if (err) + return callback(err); + + callback(null, data.order_id); + } + + const fetch = cb => this.api_query("order_create", { pair: this.pair, quantity: amount, price: price, type: type } , cb); + retry(null, fetch, processResponse); + //callback(null, 1177669837); +} + +Trader.prototype.buy = function(amount, price, callback) { + this.submitOrder('buy', amount, price, callback); +} + +Trader.prototype.sell = function(amount, price, callback) { + this.submitOrder('sell', amount, price, callback); +} + +Trader.prototype.getOrder = function(order_id, callback) { + const processResponse = (err, data) => { + + if(err) + return callback(err); + + data=data[this.pair]; + + if(data==undefined) return callback(new Error('Orders not found')); + + const order = _.find(data, function(o) { return o.order_id === +order_id }); + + if(!order) return callback(new Error('Order not found')); + + callback(undefined, { price: order.price, amount: order.amount, date: moment.unix(order.date) }); + } + + const fetch = cb => this.api_query("user_trades", { pair: this.pair} , cb); + retry(null, fetch, processResponse); +} + +Trader.prototype.checkOrder = function(order_id, callback) { + const processResponse = (err, data) => { + + if(err) + return callback(err); + + data=data[this.pair]; + + if(data==undefined) { + return callback(undefined, { executed: true, open: false }); + } + + const order = _.find(data, function(o) { return o.order_id === +order_id }); + if(!order) + return callback(undefined, { executed: true, open: false }); + + callback(undefined, { executed: false, open: true /*, filledAmount: order.startingAmount - order.amount*/ }); + } + + const fetch = cb => this.api_query("user_open_orders", { } , cb); + retry(null, fetch, processResponse); +} + +Trader.prototype.cancelOrder = function(order_id, callback) { + const processResponse = (err, data) => { + if (err) { + return callback(err); + } + + return callback(undefined, false); + } + + const fetch = cb => this.api_query("order_cancel", { order_id } , cb); + retry(null, fetch, processResponse); +} + +Trader.prototype.getPortfolio = function(callback) { + const processResponse = (err, data) => { + if (err) return callback(err); + + data=data["balances"]; + const balances = _.map(data, function (v,k){ return {name: k, amount: +v};}); + const portfolio = balances.filter(c => c.name===this.asset || c.name===this.currency) + + callback(undefined, portfolio); + }; + + const fetch = cb => this.api_query("user_info", {}, cb); + retry(null, fetch, processResponse); +} + +Trader.getCapabilities = function () { + return { + name: 'EXMO', + slug: 'exmo', + currencies: marketData.currencies, + assets: marketData.assets, + markets: marketData.markets, + requires: ['key', 'secret'], + providesHistory: false, + tid: 'tid', + tradable: true, + gekkoBroker: 0.6 + }; +} + +module.exports = Trader; diff --git a/exchange/wrappers/gdax.js b/exchange/wrappers/gdax.js new file mode 100644 index 0000000..dbe192a --- /dev/null +++ b/exchange/wrappers/gdax.js @@ -0,0 +1,430 @@ +const Gdax = require('gdax'); +const _ = require('lodash'); +const moment = require('moment'); + +const errors = require('../exchangeErrors'); +const retry = require('../exchangeUtils').retry; + +const BATCH_SIZE = 100; +const QUERY_DELAY = 350; + +const marketData = require('./coinbase-markets.json'); + +const Trader = function(config) { + this.post_only = true; + this.use_sandbox = false; + this.name = 'GDAX'; + this.scanback = false; + this.scanbackTid = 0; + this.scanbackResults = []; + this.asset = config.asset; + this.currency = config.currency; + + this.api_url = 'https://api.pro.coinbase.com'; + this.api_sandbox_url = 'https://api-public.sandbox.pro.coinbase.com'; + + if (_.isObject(config)) { + this.key = config.key; + this.secret = config.secret; + this.passphrase = config.passphrase; + + this.pair = [config.asset, config.currency].join('-').toUpperCase(); + this.post_only = + typeof config.post_only !== 'undefined' ? config.post_only : true; + + if (config.sandbox) { + this.use_sandbox = config.sandbox; + } + + } + + this.gdax_public = new Gdax.PublicClient( + this.use_sandbox ? this.api_sandbox_url : undefined + ); + this.gdax = new Gdax.AuthenticatedClient( + this.key, + this.secret, + this.passphrase, + this.use_sandbox ? this.api_sandbox_url : undefined + ); +}; + +const recoverableErrors = [ + 'SOCKETTIMEDOUT', + 'TIMEDOUT', + 'CONNRESET', + 'CONNREFUSED', + 'NOTFOUND', + 'Rate limit exceeded', + 'Response code 5', + 'GDAX is currently under maintenance.', + 'HTTP 408 Error', + 'HTTP 504 Error', + 'HTTP 503 Error', + 'socket hang up', + 'EHOSTUNREACH', + 'EAI_AGAIN', + 'ENETUNREACH' +]; + +const includes = (str, list) => { + if(!_.isString(str)) + return false; + + return _.some(list, item => str.includes(item)); +} + +Trader.prototype.processResponse = function(method, next) { + return (error, response, body) => { + if(!error && body && !_.isEmpty(body.message)) { + error = new Error(body.message); + } + + if( + response && + response.statusCode < 200 && + response.statusCode >= 300 + ) { + error = new Error(`Response code ${response.statusCode}`); + } + + if(error) { + if(includes(error.message, recoverableErrors)) { + error.notFatal = true; + error.backoffDelay = 1000; + } + + if( + ['buy', 'sell'].includes(method) && + error.message.includes('Insufficient funds') + ) { + error.retry = 10; + } + + return next(error); + } + + return next(undefined, body); + } +} + +Trader.prototype.getPortfolio = function(callback) { + const result = (err, data) => { + if (err) return callback(err); + + var portfolio = data.map(function(account) { + return { + name: account.currency.toUpperCase(), + amount: parseFloat(account.available), + }; + }); + callback(undefined, portfolio); + }; + + const fetch = cb => + this.gdax.getAccounts(this.processResponse('getPortfolio', cb)); + retry(null, fetch, result); +}; + +Trader.prototype.getTicker = function(callback) { + const result = (err, data) => { + if (err) return callback(err); + callback(undefined, { bid: +data.bid, ask: +data.ask }); + }; + + const fetch = cb => + this.gdax_public.getProductTicker(this.pair, this.processResponse('getTicker', cb)); + retry(null, fetch, result); +}; + +Trader.prototype.getFee = function(callback) { + //https://www.gdax.com/fees + // const fee = this.asset == 'BTC' ? 0.0025 : 0.003; + const fee = 0; + + //There is no maker fee, not sure if we need taker fee here + //If post only is enabled, gdax only does maker trades which are free + callback(undefined, this.post_only ? 0 : fee); +}; + +Trader.prototype.roundPrice = function(price) { + return this.getMaxDecimalsNumber(price, this.currency == 'BTC' ? 5 : 2); +} + +Trader.prototype.roundAmount = function(amount) { + return this.getMaxDecimalsNumber(amount); +} + +Trader.prototype.buy = function(amount, price, callback) { + const buyParams = { + price: this.getMaxDecimalsNumber(price, this.currency == 'BTC' ? 5 : 2), + size: this.getMaxDecimalsNumber(amount), + product_id: this.pair, + post_only: this.post_only, + }; + + const result = (err, data) => { + if (err) { + console.log({buyParams}, err.message); + return callback(err); + } + callback(undefined, data.id); + }; + + const fetch = cb => + this.gdax.buy(buyParams, this.processResponse('buy', cb)); + retry(null, fetch, result); +}; + +Trader.prototype.sell = function(amount, price, callback) { + const sellParams = { + price: this.getMaxDecimalsNumber(price, this.currency == 'BTC' ? 5 : 2), + size: this.getMaxDecimalsNumber(amount), + product_id: this.pair, + post_only: this.post_only, + }; + + const result = (err, data) => { + if (err) { + console.log({sellParams}, err.message); + return callback(err); + } + + if(data.message && data.message.includes('Insufficient funds')) { + err = new Error(data.message); + err.retryOnce = true; + return callback(err); + } + + callback(undefined, data.id); + }; + + const fetch = cb => + this.gdax.sell(sellParams, this.processResponse('sell', cb)); + retry(null, fetch, result); +}; + +Trader.prototype.checkOrder = function(order, callback) { + const result = (err, data) => { + if (err) return callback(err); + + // @link: + // https://stackoverflow.com/questions/48132078/available-gdax-order-statuses-and-meanings + var status = data.status; + if(status == 'pending') { + // technically not open yet, but will be soon + return callback(undefined, { executed: false, open: true, filledAmount: 0 }); + } if (status === 'done' || status === 'settled') { + return callback(undefined, { executed: true, open: false }); + } else if (status === 'rejected') { + return callback(undefined, { executed: false, open: false }); + } else if(status === 'open' || status === 'active') { + return callback(undefined, { executed: false, open: true, filledAmount: parseFloat(data.filled_size) }); + } + + callback(new Error('Unknown status ' + status)); + }; + + const fetch = cb => + this.gdax.getOrder(order, this.processResponse('checkOrder', cb)); + retry(null, fetch, result); +}; + +Trader.prototype.getOrder = function(order, callback) { + const result = (err, data) => { + if (err) return callback(err); + + const price = parseFloat(data.price); + const amount = parseFloat(data.filled_size); + const date = moment(data.done_at); + const fees = { + // you always pay fee in the base currency on gdax + [this.currency]: +data.fill_fees + } + const feePercent = +data.fill_fees / price / amount * 100; + + callback(undefined, { price, amount, date, fees, feePercent }); + }; + + const fetch = cb => + this.gdax.getOrder(order, this.processResponse('getOrder', cb)); + retry(null, fetch, result); +}; + +Trader.prototype.cancelOrder = function(order, callback) { + // callback for cancelOrder should be true if the order was already filled, otherwise false + const result = (err, data) => { + if(err) { + return callback(null, true); // need to catch the specific error but usually an error on cancel means it was filled + } + + return callback(null, false); + }; + + const fetch = cb => + this.gdax.cancelOrder(order, this.processResponse('cancelOrder', cb)); + retry(null, fetch, result); +}; + +Trader.prototype.getTrades = function(since, callback, descending) { + var lastScan = 0; + + const handle = function(err, data) { + if (err) return callback(err); + + var result = _.map(data, function(trade) { + return { + tid: trade.trade_id, + amount: parseFloat(trade.size), + date: moment.utc(trade.time).format('X'), + price: parseFloat(trade.price), + }; + }); + + if (this.scanback) { + var last = _.last(data); + var first = _.first(data); + + // Try to find trade id matching the since date + if (!this.scanbackTid) { + // either scan for new ones or we found it. + if (moment.utc(last.time) < moment.utc(since)) { + this.scanbackTid = last.trade_id; + } else { + console.log('Scanning backwards...' + last.time); + setTimeout(() => { + let handler = cb => + this.gdax_public.getProductTrades( + this.pair, + { + after: last.trade_id - BATCH_SIZE * lastScan, + limit: BATCH_SIZE, + }, + this.processResponse('getTrades', cb) + ); + retry( + retryForever, + _.bind(handler, this), + _.bind(process, this) + ); + }, QUERY_DELAY); + lastScan++; + if (lastScan > 100) { + lastScan = 10; + } + } + } + + if (this.scanbackTid) { + // if scanbackTid is set we need to move forward again + console.log( + 'Backwards: ' + + last.time + + ' (' + + last.trade_id + + ') to ' + + first.time + + ' (' + + first.trade_id + + ')' + ); + + this.scanbackResults = this.scanbackResults.concat(result.reverse()); + + if (this.scanbackTid != first.trade_id) { + this.scanbackTid = first.trade_id; + setTimeout(() => { + let handler = cb => + this.gdax_public.getProductTrades( + this.pair, + { after: this.scanbackTid + BATCH_SIZE + 1, limit: BATCH_SIZE }, + this.processResponse('getTrades', cb) + ); + retry( + retryForever, + _.bind(handler, this), + _.bind(process, this) + ); + }, QUERY_DELAY); + } else { + this.scanback = false; + this.scanbackTid = 0; + + console.log('Scan finished: data found:' + this.scanbackResults.length); + callback(null, this.scanbackResults); + + this.scanbackResults = []; + } + } + } else { + callback(null, result.reverse()); + } + }; + + if (since || this.scanback) { + this.scanback = true; + if (this.scanbackTid) { + let handler = cb => + this.gdax_public.getProductTrades( + this.pair, + { after: this.scanbackTid + BATCH_SIZE + 1, limit: BATCH_SIZE }, + this.processResponse('getTrades', cb) + ); + retry( + retryForever, + _.bind(handler, this), + _.bind(process, this) + ); + } else if (since) { + console.log('Scanning back in the history needed...', since); + } + } + + const fetch = cb => + this.gdax_public.getProductTrades( + this.pair, + { limit: BATCH_SIZE }, + this.processResponse('getTrades', cb) + ); + retry(null, fetch, handle); +}; + +Trader.prototype.getMaxDecimalsNumber = function(number, decimalLimit = 8) { + var decimalNumber = parseFloat(number); + + // The ^-?\d*\. strips off any sign, integer portion, and decimal point + // leaving only the decimal fraction. + // The 0+$ strips off any trailing zeroes. + var decimalCount = (+decimalNumber).toString().replace(/^-?\d*\.?|0+$/g, '') + .length; + + var decimalMultiplier = 1; + for (i = 0; i < decimalLimit; i++) { + decimalMultiplier *= 10; + } + + return decimalCount <= decimalLimit + ? decimalNumber.toString() + : ( + Math.floor(decimalNumber * decimalMultiplier) / decimalMultiplier + ).toFixed(decimalLimit); +}; + +Trader.getCapabilities = function() { + return { + name: 'GDAX', + slug: 'gdax', + currencies: marketData.currencies, + assets: marketData.assets, + markets: marketData.markets, + requires: ['key', 'secret', 'passphrase'], + providesHistory: 'date', + providesFullHistory: true, + tid: 'tid', + tradable: true, + forceReorderDelay: false, + gekkoBroker: 0.6 + }; +}; + +module.exports = Trader; \ No newline at end of file diff --git a/exchange/wrappers/gemini.js.old b/exchange/wrappers/gemini.js.old new file mode 100644 index 0000000..553321e --- /dev/null +++ b/exchange/wrappers/gemini.js.old @@ -0,0 +1,228 @@ +var Gemini = require('gemini-exchange-coffee-api/lib/gemini'); +var util = require('../core/util.js'); +var _ = require('lodash'); +var moment = require('moment'); +var log = require('../core/log'); + +var Trader = function(config) { + _.bindAll(this); + if(_.isObject(config)) { + this.key = config.key; + this.secret = config.secret; + } + this.name = 'Gemini'; + this.balance; + this.price; + this.asset = config.asset; + this.currency = config.currency; + this.pair = this.asset + this.currency; + this.gemini = new Gemini(this.key, this.secret); +} + +// if the exchange errors we try the same call again after +// waiting 10 seconds +Trader.prototype.retry = function(method, args) { + var wait = +moment.duration(10, 'seconds'); + log.debug(this.name, 'returned an error, retrying..'); + + var self = this; + + // make sure the callback (and any other fn) + // is bound to Trader + _.each(args, function(arg, i) { + if(_.isFunction(arg)) + args[i] = _.bind(arg, self); + }); + + // run the failed method again with the same + // arguments after wait + setTimeout( + function() { method.apply(self, args) }, + wait + ); +} + +Trader.prototype.getPortfolio = function(callback) { + var args = _.toArray(arguments); + this.gemini.wallet_balances(function (err, data, body) { + + if(err && err.message === '401') { + let e = 'Gemini replied with an unauthorized error. '; + e += 'Double check whether your API key is correct.'; + util.die(e); + } + + if(err || !data) + return this.retry(this.getPortfolio, args); + + // We are only interested in funds in the "exchange" wallet + data = data.filter(c => c.type === 'exchange'); + + const asset = _.find(data, c => c.currency.toUpperCase() === this.asset); + const currency = _.find(data, c => c.currency.toUpperCase() === this.currency); + + let assetAmount, currencyAmount; + + if(_.isObject(asset) && _.isNumber(+asset.available) && !_.isNaN(+asset.available)) + assetAmount = +asset.available; + else { + log.error(`Gemini did not provide ${this.asset} amount, assuming 0`); + assetAmount = 0; + } + + if(_.isObject(currency) && _.isNumber(+currency.available) && !_.isNaN(+currency.available)) + currencyAmount = +currency.available; + else { + log.error(`Gemini did not provide ${this.currency} amount, assuming 0`); + currencyAmount = 0; + } + + const portfolio = [ + { name: this.asset, amount: assetAmount }, + { name: this.currency, amount: currencyAmount }, + ]; + + callback(err, portfolio); + }.bind(this)); +} + +Trader.prototype.getTicker = function(callback) { + var args = _.toArray(arguments); + // the function that will handle the API callback + var process = function(err, data, body) { + if (err) + return this.retry(this.getTicker(args)); + + // whenever we reach this point we have valid + // data, the callback is still the same since + // we are inside the same javascript scope. + callback(err, {bid: +data.bid, ask: +data.ask}) + }.bind(this); + this.gemini.ticker(this.pair, process); +} + +// This assumes that only limit orders are being placed, so fees are the +// "maker fee" of 0.1%. It does not take into account volume discounts. +Trader.prototype.getFee = function(callback) { + var makerFee = 0.25; + callback(false, makerFee / 100); +} + +Trader.prototype.submit_order = function(type, amount, price, callback) { + var args = _.toArray(arguments); + + amount = Math.floor(amount*100000000)/100000000; + this.gemini.new_order( + this.pair, + amount + '', + price + '', + this.name.toLowerCase(), + type, + 'exchange limit', + function (err, data, body) { + if (err) { + log.error('unable to ' + type, err, body); + return this.retry(this.submit_order, args); + } + + callback(err, data.order_id); + }.bind(this)); +} + +Trader.prototype.buy = function(amount, price, callback) { + this.submit_order('buy', amount, price, callback); +} + +Trader.prototype.sell = function(amount, price, callback) { + this.submit_order('sell', amount, price, callback); +} + +Trader.prototype.checkOrder = function(order_id, callback) { + var args = _.toArray(arguments); + this.gemini.order_status(order_id, function (err, data, body) { + + if(err || !data) + return this.retry(this.checkOrder, arguments); + + callback(err, !data.is_live); + }.bind(this)); +} + + +Trader.prototype.getOrder = function(order, callback) { + var args = _.toArray(arguments); + var get = function(err, data) { + if(err || !data) + return this.retry(this.getOrder, arguments); + + var price = parseFloat(data.avg_execution_price); + var amount = parseFloat(data.executed_amount); + var date = moment.unix(data.timestamp); + + callback(undefined, {price, amount, date}); + }.bind(this); + + this.gemini.order_status(order, get); +} + + +Trader.prototype.cancelOrder = function(order_id, callback) { + var args = _.toArray(arguments); + this.gemini.cancel_order(order_id, function (err, data, body) { + if (err || !data) { + // gemini way of telling it was already cancelled.. + if(err.message === 'Order could not be cancelled.') + return callback(); + + log.error('unable to cancel order', order_id, '(', err, data, '), retrying...'); + return this.retry(this.cancelOrder, args); + } + + return callback(); + }.bind(this)); +} + +Trader.prototype.getTrades = function(since, callback, descending) { + var args = _.toArray(arguments); + + var path = this.pair; + if(since) + path += '?limit_trades=2000'; + + this.gemini.trades(path, function(err, data) { + if (err) + return this.retry(this.getTrades, args); + + var trades = _.map(data, function(trade) { + return { + tid: trade.tid, + date: trade.timestamp, + price: +trade.price, + amount: +trade.amount + } + }); + + callback(null, descending ? trades : trades.reverse()); + }.bind(this)); +} + +Trader.getCapabilities = function () { + return { + name: 'Gemini', + slug: 'gemini', + currencies: ['USD', 'BTC'], + assets: ['BTC', 'ETH'], + markets: [ + + { pair: ['USD', 'BTC'], minimalOrder: { amount: 0.01, unit: 'asset' } }, + { pair: ['USD', 'ETH'], minimalOrder: { amount: 0.01, unit: 'asset' } }, + { pair: ['BTC', 'ETH'], minimalOrder: { amount: 0.01, unit: 'asset' } }, + + ], + requires: ['key', 'secret'], + tid: 'tid', + tradable: true + }; +} + +module.exports = Trader; diff --git a/exchange/wrappers/kraken-markets.json b/exchange/wrappers/kraken-markets.json new file mode 100644 index 0000000..f763ecf --- /dev/null +++ b/exchange/wrappers/kraken-markets.json @@ -0,0 +1,1259 @@ +{ + "assets": [ + "ADA", + "BCH", + "DASH", + "EOS", + "GNO", + "QTUM", + "USDT", + "ETC", + "ETH", + "ICN", + "LTC", + "MLN", + "REP", + "XTZ", + "XBT", + "XDG", + "XLM", + "XMR", + "XRP", + "ZEC" + ], + "currencies": [ + "CAD", + "ETH", + "EUR", + "USD", + "XBT", + "GBP", + "JPY" + ], + "markets": [ + { + "pair": [ + "CAD", + "ADA" + ], + "prefixed": [ + "ZCAD", + "ADA" + ], + "book": "ADACAD", + "minimalOrder": { + "amount": "1", + "unit": "asset" + }, + "pricePrecision": 6, + "amountPrecision": 8 + }, + { + "pair": [ + "ETH", + "ADA" + ], + "prefixed": [ + "XETH", + "ADA" + ], + "book": "ADAETH", + "minimalOrder": { + "amount": "1", + "unit": "asset" + }, + "pricePrecision": 7, + "amountPrecision": 8 + }, + { + "pair": [ + "EUR", + "ADA" + ], + "prefixed": [ + "ZEUR", + "ADA" + ], + "book": "ADAEUR", + "minimalOrder": { + "amount": "1", + "unit": "asset" + }, + "pricePrecision": 6, + "amountPrecision": 8 + }, + { + "pair": [ + "USD", + "ADA" + ], + "prefixed": [ + "ZUSD", + "ADA" + ], + "book": "ADAUSD", + "minimalOrder": { + "amount": "1", + "unit": "asset" + }, + "pricePrecision": 6, + "amountPrecision": 8 + }, + { + "pair": [ + "XBT", + "ADA" + ], + "prefixed": [ + "XXBT", + "ADA" + ], + "book": "ADAXBT", + "minimalOrder": { + "amount": "1", + "unit": "asset" + }, + "pricePrecision": 8, + "amountPrecision": 8 + }, + { + "pair": [ + "EUR", + "BCH" + ], + "prefixed": [ + "ZEUR", + "BCH" + ], + "book": "BCHEUR", + "minimalOrder": { + "amount": "0.002", + "unit": "asset" + }, + "pricePrecision": 1, + "amountPrecision": 8 + }, + { + "pair": [ + "USD", + "BCH" + ], + "prefixed": [ + "ZUSD", + "BCH" + ], + "book": "BCHUSD", + "minimalOrder": { + "amount": "0.002", + "unit": "asset" + }, + "pricePrecision": 1, + "amountPrecision": 8 + }, + { + "pair": [ + "XBT", + "BCH" + ], + "prefixed": [ + "XXBT", + "BCH" + ], + "book": "BCHXBT", + "minimalOrder": { + "amount": "0.002", + "unit": "asset" + }, + "pricePrecision": 5, + "amountPrecision": 8 + }, + { + "pair": [ + "EUR", + "DASH" + ], + "prefixed": [ + "ZEUR", + "DASH" + ], + "book": "DASHEUR", + "minimalOrder": { + "amount": "0.03", + "unit": "asset" + }, + "pricePrecision": 3, + "amountPrecision": 8 + }, + { + "pair": [ + "USD", + "DASH" + ], + "prefixed": [ + "ZUSD", + "DASH" + ], + "book": "DASHUSD", + "minimalOrder": { + "amount": "0.03", + "unit": "asset" + }, + "pricePrecision": 3, + "amountPrecision": 8 + }, + { + "pair": [ + "XBT", + "DASH" + ], + "prefixed": [ + "XXBT", + "DASH" + ], + "book": "DASHXBT", + "minimalOrder": { + "amount": "0.03", + "unit": "asset" + }, + "pricePrecision": 5, + "amountPrecision": 8 + }, + { + "pair": [ + "ETH", + "EOS" + ], + "prefixed": [ + "XETH", + "EOS" + ], + "book": "EOSETH", + "minimalOrder": { + "amount": "3.0", + "unit": "asset" + }, + "pricePrecision": 6, + "amountPrecision": 8 + }, + { + "pair": [ + "EUR", + "EOS" + ], + "prefixed": [ + "ZEUR", + "EOS" + ], + "book": "EOSEUR", + "minimalOrder": { + "amount": "3.0", + "unit": "asset" + }, + "pricePrecision": 4, + "amountPrecision": 8 + }, + { + "pair": [ + "USD", + "EOS" + ], + "prefixed": [ + "ZUSD", + "EOS" + ], + "book": "EOSUSD", + "minimalOrder": { + "amount": "3.0", + "unit": "asset" + }, + "pricePrecision": 4, + "amountPrecision": 8 + }, + { + "pair": [ + "XBT", + "EOS" + ], + "prefixed": [ + "XXBT", + "EOS" + ], + "book": "EOSXBT", + "minimalOrder": { + "amount": "3.0", + "unit": "asset" + }, + "pricePrecision": 7, + "amountPrecision": 8 + }, + { + "pair": [ + "ETH", + "GNO" + ], + "prefixed": [ + "XETH", + "GNO" + ], + "book": "GNOETH", + "minimalOrder": { + "amount": "0.03", + "unit": "asset" + }, + "pricePrecision": 4, + "amountPrecision": 8 + }, + { + "pair": [ + "EUR", + "GNO" + ], + "prefixed": [ + "ZEUR", + "GNO" + ], + "book": "GNOEUR", + "minimalOrder": { + "amount": "0.03", + "unit": "asset" + }, + "pricePrecision": 2, + "amountPrecision": 8 + }, + { + "pair": [ + "USD", + "GNO" + ], + "prefixed": [ + "ZUSD", + "GNO" + ], + "book": "GNOUSD", + "minimalOrder": { + "amount": "0.03", + "unit": "asset" + }, + "pricePrecision": 2, + "amountPrecision": 8 + }, + { + "pair": [ + "XBT", + "GNO" + ], + "prefixed": [ + "XXBT", + "GNO" + ], + "book": "GNOXBT", + "minimalOrder": { + "amount": "0.03", + "unit": "asset" + }, + "pricePrecision": 5, + "amountPrecision": 8 + }, + { + "pair": [ + "CAD", + "QTUM" + ], + "prefixed": [ + "ZCAD", + "QTUM" + ], + "book": "QTUMCAD", + "minimalOrder": { + "amount": "0.1", + "unit": "asset" + }, + "pricePrecision": 5, + "amountPrecision": 8 + }, + { + "pair": [ + "ETH", + "QTUM" + ], + "prefixed": [ + "XETH", + "QTUM" + ], + "book": "QTUMETH", + "minimalOrder": { + "amount": "0.1", + "unit": "asset" + }, + "pricePrecision": 7, + "amountPrecision": 8 + }, + { + "pair": [ + "EUR", + "QTUM" + ], + "prefixed": [ + "ZEUR", + "QTUM" + ], + "book": "QTUMEUR", + "minimalOrder": { + "amount": "0.1", + "unit": "asset" + }, + "pricePrecision": 5, + "amountPrecision": 8 + }, + { + "pair": [ + "USD", + "QTUM" + ], + "prefixed": [ + "ZUSD", + "QTUM" + ], + "book": "QTUMUSD", + "minimalOrder": { + "amount": "0.1", + "unit": "asset" + }, + "pricePrecision": 5, + "amountPrecision": 8 + }, + { + "pair": [ + "XBT", + "QTUM" + ], + "prefixed": [ + "XXBT", + "QTUM" + ], + "book": "QTUMXBT", + "minimalOrder": { + "amount": "0.1", + "unit": "asset" + }, + "pricePrecision": 7, + "amountPrecision": 8 + }, + { + "pair": [ + "USD", + "USDT" + ], + "prefixed": [ + "ZUSD", + "USDT" + ], + "book": "USDTZUSD", + "minimalOrder": { + "amount": "5", + "unit": "asset" + }, + "pricePrecision": 4, + "amountPrecision": 8 + }, + { + "pair": [ + "ETH", + "ETC" + ], + "prefixed": [ + "XETH", + "XETC" + ], + "book": "XETCXETH", + "minimalOrder": { + "amount": "0.3", + "unit": "asset" + }, + "pricePrecision": 6, + "amountPrecision": 8 + }, + { + "pair": [ + "XBT", + "ETC" + ], + "prefixed": [ + "XXBT", + "XETC" + ], + "book": "XETCXXBT", + "minimalOrder": { + "amount": "0.3", + "unit": "asset" + }, + "pricePrecision": 6, + "amountPrecision": 8 + }, + { + "pair": [ + "EUR", + "ETC" + ], + "prefixed": [ + "ZEUR", + "XETC" + ], + "book": "XETCZEUR", + "minimalOrder": { + "amount": "0.3", + "unit": "asset" + }, + "pricePrecision": 3, + "amountPrecision": 8 + }, + { + "pair": [ + "USD", + "ETC" + ], + "prefixed": [ + "ZUSD", + "XETC" + ], + "book": "XETCZUSD", + "minimalOrder": { + "amount": "0.3", + "unit": "asset" + }, + "pricePrecision": 3, + "amountPrecision": 8 + }, + { + "pair": [ + "XBT", + "ETH" + ], + "prefixed": [ + "XXBT", + "XETH" + ], + "book": "XETHXXBT", + "minimalOrder": { + "amount": "0.02", + "unit": "asset" + }, + "pricePrecision": 5, + "amountPrecision": 8 + }, + { + "pair": [ + "CAD", + "ETH" + ], + "prefixed": [ + "ZCAD", + "XETH" + ], + "book": "XETHZCAD", + "minimalOrder": { + "amount": "0.02", + "unit": "asset" + }, + "pricePrecision": 2, + "amountPrecision": 8 + }, + { + "pair": [ + "EUR", + "ETH" + ], + "prefixed": [ + "ZEUR", + "XETH" + ], + "book": "XETHZEUR", + "minimalOrder": { + "amount": "0.02", + "unit": "asset" + }, + "pricePrecision": 2, + "amountPrecision": 8 + }, + { + "pair": [ + "GBP", + "ETH" + ], + "prefixed": [ + "ZGBP", + "XETH" + ], + "book": "XETHZGBP", + "minimalOrder": { + "amount": "0.02", + "unit": "asset" + }, + "pricePrecision": 2, + "amountPrecision": 8 + }, + { + "pair": [ + "JPY", + "ETH" + ], + "prefixed": [ + "ZJPY", + "XETH" + ], + "book": "XETHZJPY", + "minimalOrder": { + "amount": "0.02", + "unit": "asset" + }, + "pricePrecision": 0, + "amountPrecision": 8 + }, + { + "pair": [ + "USD", + "ETH" + ], + "prefixed": [ + "ZUSD", + "XETH" + ], + "book": "XETHZUSD", + "minimalOrder": { + "amount": "0.02", + "unit": "asset" + }, + "pricePrecision": 2, + "amountPrecision": 8 + }, + { + "pair": [ + "ETH", + "ICN" + ], + "prefixed": [ + "XETH", + "XICN" + ], + "book": "XICNXETH", + "minimalOrder": { + "amount": "2", + "unit": "asset" + }, + "pricePrecision": 6, + "amountPrecision": 8 + }, + { + "pair": [ + "XBT", + "ICN" + ], + "prefixed": [ + "XXBT", + "XICN" + ], + "book": "XICNXXBT", + "minimalOrder": { + "amount": "2", + "unit": "asset" + }, + "pricePrecision": 6, + "amountPrecision": 8 + }, + { + "pair": [ + "XBT", + "LTC" + ], + "prefixed": [ + "XXBT", + "XLTC" + ], + "book": "XLTCXXBT", + "minimalOrder": { + "amount": "0.1", + "unit": "asset" + }, + "pricePrecision": 6, + "amountPrecision": 8 + }, + { + "pair": [ + "EUR", + "LTC" + ], + "prefixed": [ + "ZEUR", + "XLTC" + ], + "book": "XLTCZEUR", + "minimalOrder": { + "amount": "0.1", + "unit": "asset" + }, + "pricePrecision": 2, + "amountPrecision": 8 + }, + { + "pair": [ + "USD", + "LTC" + ], + "prefixed": [ + "ZUSD", + "XLTC" + ], + "book": "XLTCZUSD", + "minimalOrder": { + "amount": "0.1", + "unit": "asset" + }, + "pricePrecision": 2, + "amountPrecision": 8 + }, + { + "pair": [ + "ETH", + "MLN" + ], + "prefixed": [ + "XETH", + "XMLN" + ], + "book": "XMLNXETH", + "minimalOrder": { + "amount": "0.1", + "unit": "asset" + }, + "pricePrecision": 5, + "amountPrecision": 8 + }, + { + "pair": [ + "XBT", + "MLN" + ], + "prefixed": [ + "XXBT", + "XMLN" + ], + "book": "XMLNXXBT", + "minimalOrder": { + "amount": "0.1", + "unit": "asset" + }, + "pricePrecision": 6, + "amountPrecision": 8 + }, + { + "pair": [ + "ETH", + "REP" + ], + "prefixed": [ + "XETH", + "XREP" + ], + "book": "XREPXETH", + "minimalOrder": { + "amount": "0.3", + "unit": "asset" + }, + "pricePrecision": 5, + "amountPrecision": 8 + }, + { + "pair": [ + "XBT", + "REP" + ], + "prefixed": [ + "XXBT", + "XREP" + ], + "book": "XREPXXBT", + "minimalOrder": { + "amount": "0.3", + "unit": "asset" + }, + "pricePrecision": 6, + "amountPrecision": 8 + }, + { + "pair": [ + "EUR", + "REP" + ], + "prefixed": [ + "ZEUR", + "XREP" + ], + "book": "XREPZEUR", + "minimalOrder": { + "amount": "0.3", + "unit": "asset" + }, + "pricePrecision": 3, + "amountPrecision": 8 + }, + { + "pair": [ + "USD", + "REP" + ], + "prefixed": [ + "ZUSD", + "XREP" + ], + "book": "XREPZUSD", + "minimalOrder": { + "amount": "0.3", + "unit": "asset" + }, + "pricePrecision": 3, + "amountPrecision": 8 + }, + { + "pair": [ + "CAD", + "XTZ" + ], + "prefixed": [ + "ZCAD", + "XTZ" + ], + "book": "XTZCAD", + "minimalOrder": { + "amount": "1", + "unit": "asset" + }, + "pricePrecision": 4, + "amountPrecision": 8 + }, + { + "pair": [ + "ETH", + "XTZ" + ], + "prefixed": [ + "XETH", + "XTZ" + ], + "book": "XTZETH", + "minimalOrder": { + "amount": "1", + "unit": "asset" + }, + "pricePrecision": 7, + "amountPrecision": 8 + }, + { + "pair": [ + "EUR", + "XTZ" + ], + "prefixed": [ + "ZEUR", + "XTZ" + ], + "book": "XTZEUR", + "minimalOrder": { + "amount": "1", + "unit": "asset" + }, + "pricePrecision": 4, + "amountPrecision": 8 + }, + { + "pair": [ + "USD", + "XTZ" + ], + "prefixed": [ + "ZUSD", + "XTZ" + ], + "book": "XTZUSD", + "minimalOrder": { + "amount": "1", + "unit": "asset" + }, + "pricePrecision": 4, + "amountPrecision": 8 + }, + { + "pair": [ + "XBT", + "XTZ" + ], + "prefixed": [ + "XXBT", + "XTZ" + ], + "book": "XTZXBT", + "minimalOrder": { + "amount": "1", + "unit": "asset" + }, + "pricePrecision": 7, + "amountPrecision": 8 + }, + { + "pair": [ + "CAD", + "XBT" + ], + "prefixed": [ + "ZCAD", + "XXBT" + ], + "book": "XXBTZCAD", + "minimalOrder": { + "amount": "0.002", + "unit": "asset" + }, + "pricePrecision": 1, + "amountPrecision": 8 + }, + { + "pair": [ + "EUR", + "XBT" + ], + "prefixed": [ + "ZEUR", + "XXBT" + ], + "book": "XXBTZEUR", + "minimalOrder": { + "amount": "0.002", + "unit": "asset" + }, + "pricePrecision": 1, + "amountPrecision": 8 + }, + { + "pair": [ + "GBP", + "XBT" + ], + "prefixed": [ + "ZGBP", + "XXBT" + ], + "book": "XXBTZGBP", + "minimalOrder": { + "amount": "0.002", + "unit": "asset" + }, + "pricePrecision": 1, + "amountPrecision": 8 + }, + { + "pair": [ + "JPY", + "XBT" + ], + "prefixed": [ + "ZJPY", + "XXBT" + ], + "book": "XXBTZJPY", + "minimalOrder": { + "amount": "0.002", + "unit": "asset" + }, + "pricePrecision": 0, + "amountPrecision": 8 + }, + { + "pair": [ + "USD", + "XBT" + ], + "prefixed": [ + "ZUSD", + "XXBT" + ], + "book": "XXBTZUSD", + "minimalOrder": { + "amount": "0.002", + "unit": "asset" + }, + "pricePrecision": 1, + "amountPrecision": 8 + }, + { + "pair": [ + "XBT", + "XDG" + ], + "prefixed": [ + "XXBT", + "XXDG" + ], + "book": "XXDGXXBT", + "minimalOrder": { + "amount": "3000", + "unit": "asset" + }, + "pricePrecision": 8, + "amountPrecision": 8 + }, + { + "pair": [ + "XBT", + "XLM" + ], + "prefixed": [ + "XXBT", + "XXLM" + ], + "book": "XXLMXXBT", + "minimalOrder": { + "amount": "30", + "unit": "asset" + }, + "pricePrecision": 8, + "amountPrecision": 8 + }, + { + "pair": [ + "EUR", + "XLM" + ], + "prefixed": [ + "ZEUR", + "XXLM" + ], + "book": "XXLMZEUR", + "minimalOrder": { + "amount": "30", + "unit": "asset" + }, + "pricePrecision": 6, + "amountPrecision": 8 + }, + { + "pair": [ + "USD", + "XLM" + ], + "prefixed": [ + "ZUSD", + "XXLM" + ], + "book": "XXLMZUSD", + "minimalOrder": { + "amount": "30", + "unit": "asset" + }, + "pricePrecision": 6, + "amountPrecision": 8 + }, + { + "pair": [ + "XBT", + "XMR" + ], + "prefixed": [ + "XXBT", + "XXMR" + ], + "book": "XXMRXXBT", + "minimalOrder": { + "amount": "0.1", + "unit": "asset" + }, + "pricePrecision": 6, + "amountPrecision": 8 + }, + { + "pair": [ + "EUR", + "XMR" + ], + "prefixed": [ + "ZEUR", + "XXMR" + ], + "book": "XXMRZEUR", + "minimalOrder": { + "amount": "0.1", + "unit": "asset" + }, + "pricePrecision": 2, + "amountPrecision": 8 + }, + { + "pair": [ + "USD", + "XMR" + ], + "prefixed": [ + "ZUSD", + "XXMR" + ], + "book": "XXMRZUSD", + "minimalOrder": { + "amount": "0.1", + "unit": "asset" + }, + "pricePrecision": 2, + "amountPrecision": 8 + }, + { + "pair": [ + "XBT", + "XRP" + ], + "prefixed": [ + "XXBT", + "XXRP" + ], + "book": "XXRPXXBT", + "minimalOrder": { + "amount": "30", + "unit": "asset" + }, + "pricePrecision": 8, + "amountPrecision": 8 + }, + { + "pair": [ + "CAD", + "XRP" + ], + "prefixed": [ + "ZCAD", + "XXRP" + ], + "book": "XXRPZCAD", + "minimalOrder": { + "amount": "30", + "unit": "asset" + }, + "pricePrecision": 5, + "amountPrecision": 8 + }, + { + "pair": [ + "EUR", + "XRP" + ], + "prefixed": [ + "ZEUR", + "XXRP" + ], + "book": "XXRPZEUR", + "minimalOrder": { + "amount": "30", + "unit": "asset" + }, + "pricePrecision": 5, + "amountPrecision": 8 + }, + { + "pair": [ + "JPY", + "XRP" + ], + "prefixed": [ + "ZJPY", + "XXRP" + ], + "book": "XXRPZJPY", + "minimalOrder": { + "amount": "30", + "unit": "asset" + }, + "pricePrecision": 3, + "amountPrecision": 8 + }, + { + "pair": [ + "USD", + "XRP" + ], + "prefixed": [ + "ZUSD", + "XXRP" + ], + "book": "XXRPZUSD", + "minimalOrder": { + "amount": "30", + "unit": "asset" + }, + "pricePrecision": 5, + "amountPrecision": 8 + }, + { + "pair": [ + "XBT", + "ZEC" + ], + "prefixed": [ + "XXBT", + "XZEC" + ], + "book": "XZECXXBT", + "minimalOrder": { + "amount": "0.03", + "unit": "asset" + }, + "pricePrecision": 5, + "amountPrecision": 8 + }, + { + "pair": [ + "EUR", + "ZEC" + ], + "prefixed": [ + "ZEUR", + "XZEC" + ], + "book": "XZECZEUR", + "minimalOrder": { + "amount": "0.03", + "unit": "asset" + }, + "pricePrecision": 3, + "amountPrecision": 8 + }, + { + "pair": [ + "JPY", + "ZEC" + ], + "prefixed": [ + "ZJPY", + "XZEC" + ], + "book": "XZECZJPY", + "minimalOrder": { + "amount": "0.03", + "unit": "asset" + }, + "pricePrecision": 3, + "amountPrecision": 8 + }, + { + "pair": [ + "USD", + "ZEC" + ], + "prefixed": [ + "ZUSD", + "XZEC" + ], + "book": "XZECZUSD", + "minimalOrder": { + "amount": "0.03", + "unit": "asset" + }, + "pricePrecision": 2, + "amountPrecision": 8 + } + ] +} \ No newline at end of file diff --git a/exchange/wrappers/kraken.js b/exchange/wrappers/kraken.js new file mode 100644 index 0000000..2c04b8b --- /dev/null +++ b/exchange/wrappers/kraken.js @@ -0,0 +1,471 @@ +const Kraken = require('kraken-api'); +const moment = require('moment'); +const _ = require('lodash'); +const exchangeUtils = require('../exchangeUtils'); +const retry = exchangeUtils.retry; +const scientificToDecimal = exchangeUtils.scientificToDecimal; + +const marketData = require('./kraken-markets.json'); + +const Trader = function(config) { + _.bindAll(this); + + if(_.isObject(config)) { + this.key = config.key; + this.secret = config.secret; + this.currency = config.currency.toUpperCase() + this.asset = config.asset.toUpperCase(); + } + + this.name = 'kraken'; + this.since = null; + + this.market = _.find(Trader.getCapabilities().markets, (market) => { + return market.pair[0] === this.currency && market.pair[1] === this.asset + }); + this.pair = this.market.book; + + this.interval = 3100; + + this.kraken = new Kraken( + this.key, + this.secret, + {timeout: +moment.duration(60, 'seconds')} + ); +} + +const recoverableErrors = [ + 'SOCKETTIMEDOUT', + 'TIMEDOUT', + 'CONNRESET', + 'CONNREFUSED', + 'NOTFOUND', + 'Service:Unavailable', + 'Request timed out', + 'Empty response', + 'API:Invalid nonce', + 'General:Temporary lockout', + 'Response code 525', + 'Service:Busy' +]; + +// errors that might mean +// the API call succeeded. +const unknownResultErrors = [ + 'Response code 502', + 'Response code 504', + 'Response code 522', + 'Response code 520', +] + +const includes = (str, list) => { + if(!_.isString(str)) + return false; + + return _.some(list, item => str.includes(item)); +} + +Trader.prototype.handleResponse = function(funcName, callback, nonMutating, payload) { + return (error, body) => { + + if(!error && !body) { + error = new Error('Empty response'); + } + + if(error) { + if(includes(error.message, recoverableErrors)) { + error.notFatal = true; + } + + if(includes(error.message, ['Rate limit exceeded'])) { + error.notFatal = true; + error.backoffDelay = 2500; + } + + if(nonMutating && includes(error.message, unknownResultErrors)) { + // this call only tried to retrieve data, safe to redo + error.notFatal = true; + } + + if(funcName === 'addOrder' && includes(error.message, unknownResultErrors)) { + + const { tradeType, amount, price } = payload; + + return setTimeout(() => { + this.getRawOpenOrders((err2, orders) => { + if(err2) { + console.log('err2', err2); + return callback(err2); + } + + _.each(orders, (o, id) => { + o.id = id; + }); + + const order = _.find(orders, o => { + if(o.descr.type !== tradeType) { + return false; + } + + const ts = moment.unix((o.opentm + '').split('.')[0]) + if(moment().diff(ts, 'm') > 10) { + return false; + } + + // string vs float + if(+o.descr.price != price) { + return false; + } + + // string vs float + if(o.vol != amount) { + return false; + } + + return true; + }); + + if(!order) { + console.log('broken add order, appears not created:', {payload, orders: JSON.stringify(orders)}); + return this.addOrder(tradeType, amount, price, callback); + } + + return callback(undefined, { catched: true, id: order.id }); + }); + }, 5000); + } + + if(funcName === 'cancelOrder' && includes(error.message, unknownResultErrors)) { + console.log('broken cancel'); + + return setTimeout(() => { + const handle = (err2, data) => { + if(err2) { + console.log('err2', err2); + return callback(err2); + } + + const order = _.get(data, `result["${payload}"]`); + + if(!_.isObject(order)) { + console.log('refetched broken cancel, cannot find order...', data); + throw 'a'; + } + + console.log(order); + + if(order.status !== 'canceled') { + console.log(new Date, 'it still exists, retrying cancel'); + return this.cancelOrder(payload, callback); + } + + console.log(new Date, 'it was canceled'); + return callback(undefined, true, { catched: true, filled: parseFloat(order.vol_exec) }); + + }; + + const reqData = {txid: payload}; + + const fetch = cb => this.kraken.api('QueryOrders', reqData, this.handleResponse('checkOrder', cb, true)); + retry(null, fetch, handle); + }, 5000); + } + + return callback(error); + } + + return callback(undefined, body); + } +}; + +Trader.prototype.getTrades = function(since, callback, descending) { + const startTs = since ? moment(since).valueOf() : null; + + const handle = (err, trades) => { + if (err) return callback(err); + + var parsedTrades = []; + _.each(trades.result[this.pair], function(trade) { + // Even when you supply 'since' you can still get more trades than you asked for, it needs to be filtered + if (_.isNull(startTs) || startTs < moment.unix(trade[2]).valueOf()) { + parsedTrades.push({ + tid: moment.unix(trade[2]).valueOf() * 1000000, + date: parseInt(Math.round(trade[2]), 10), + price: parseFloat(trade[0]), + amount: parseFloat(trade[1]) + }); + } + }, this); + + if(descending) + callback(undefined, parsedTrades.reverse()); + else + callback(undefined, parsedTrades); + }; + + const reqData = { + pair: this.pair + }; + + if(since) { + // Kraken wants a tid, which is found to be timestamp_ms * 1000000 in practice. No clear documentation on this though + reqData.since = startTs * 1000000; + } + + const fetch = cb => this.kraken.api('Trades', reqData, this.handleResponse('getTrades', cb, true)); + retry(null, fetch, handle); +}; + +Trader.prototype.getPortfolio = function(callback) { + const handle = (err, data) => { + if(err) return callback(err); + + let assetAmount = parseFloat( data.result[this.market.prefixed[1]] ); + let currencyAmount = parseFloat( data.result[this.market.prefixed[0]] ); + + if(!_.isNumber(assetAmount) || _.isNaN(assetAmount)) { + console.log(`Kraken did not return portfolio for ${this.asset}, assuming 0.`); + assetAmount = 0; + } + + if(!_.isNumber(currencyAmount) || _.isNaN(currencyAmount)) { + console.log(`Kraken did not return portfolio for ${this.currency}, assuming 0.`); + currencyAmount = 0; + } + + const portfolio = [ + { name: this.asset, amount: assetAmount }, + { name: this.currency, amount: currencyAmount } + ]; + + return callback(undefined, portfolio); + }; + + const fetch = cb => this.kraken.api('Balance', {}, this.handleResponse('getPortfolio', cb, true)); + retry(null, fetch, handle); +}; + +// This assumes that only limit orders are being placed with standard assets pairs +// It does not take into account volume discounts. +// Base maker fee is 0.16%, taker fee is 0.26%. +Trader.prototype.getFee = function(callback) { + const makerFee = 0.16; + callback(undefined, makerFee / 100); +}; + +Trader.prototype.getTicker = function(callback) { + const handle = (err, data) => { + if (err) return callback(err); + + const result = data.result[this.pair]; + const ticker = { + ask: result.a[0], + bid: result.b[0] + }; + callback(undefined, ticker); + }; + + const reqData = {pair: this.pair} + const fetch = cb => this.kraken.api('Ticker', reqData, this.handleResponse('getTicker', cb, true)); + retry(null, fetch, handle); +}; + +Trader.prototype.roundAmount = function(amount) { + return _.floor(amount, this.market.amountPrecision); +}; + +Trader.prototype.roundPrice = function(amount) { + return scientificToDecimal(_.round(amount, this.market.pricePrecision)); +}; + +Trader.prototype.addOrder = function(tradeType, amount, price, callback) { + price = this.roundPrice(price); // only round price, not amount + + const handle = (err, data) => { + if(err) { + return callback(err); + } + + let txid; + + if(data.catched) { + // handled timeout, but order was created + txid = data.id; + } else if(_.isString(data)) { + // handled timeout, order was NOT created + txid = data; + } else { + // normal flow + txid = data.result.txid[0]; + } + + callback(undefined, txid); + }; + + const reqData = { + pair: this.pair, + type: tradeType.toLowerCase(), + ordertype: 'limit', + price: price, + volume: amount + }; + + const fetch = cb => this.kraken.api('AddOrder', reqData, this.handleResponse('addOrder', cb, false, { tradeType, amount, price })); + retry(null, fetch, handle); +}; + +Trader.prototype.buy = function(amount, price, callback) { + this.addOrder('buy', amount, price, callback); +}; + +Trader.prototype.sell = function(amount, price, callback) { + this.addOrder('sell', amount, price, callback); +}; + + +Trader.prototype.getOrder = function(order, callback) { + const handle = (err, data) => { + if(err) return callback(err); + + const price = parseFloat( data.result[ order ].price ); + const amount = parseFloat( data.result[ order ].vol_exec ); + const date = moment.unix( data.result[ order ].closetm ); + + // TODO: figure out fees, kraken is reporting 0 fees: + // { 'OF6L2D-6LIKD-4OOHR7': + // { refid: null, + // userref: 0, + // status: 'closed', + // reason: null, + // opentm: 1530694339.8116, + // closetm: 1530694402.9572, + // starttm: 0, + // expiretm: 0, + // descr: [Object], + // vol: '0.00500000', + // vol_exec: '0.00500000', + // cost: '27.9', + // fee: '0', + // price: '5595.0', + // stopprice: '0.00000', + // limitprice: '0.00000', + // misc: '', + // oflags: 'fciq' } } } + + callback(undefined, { + price, + amount, + date, + feePercent: 0.16 // default for now + }); + }; + + const reqData = {txid: order}; + + const fetch = cb => this.kraken.api('QueryOrders', reqData, this.handleResponse('getOrder', cb, true)); + retry(null, fetch, handle); +} + +Trader.prototype.checkOrder = function(order, callback) { + const handle = (err, data) => { + if(err) return callback(err); + + const result = data.result[order]; + + callback(undefined, { + executed: result.vol === result.vol_exec, + open: result.status === 'open', + filledAmount: +result.vol_exec + }); + }; + + const reqData = {txid: order}; + + const fetch = cb => this.kraken.api('QueryOrders', reqData, this.handleResponse('checkOrder', cb, true)); + retry(null, fetch, handle); +}; + +Trader.prototype.cancelOrder = function(order, callback) { + const reqData = {txid: order}; + + const handle = (err, data) => { + if(err) { + if(err.message.includes('Unknown order')) { + return callback(undefined, true); + } + + // catch race condition: + // if we cancel, that request times out + // we recheck: it's live BUT then the + // timeout request goes through nonetheless + // (only times out behind cloudflare) + if(err.message.includes('Invalid order')) { + return callback(undefined, true); + } + + return callback(err) + } + + if(data.filled) { + callback(undefined, false, data); + } + + callback(undefined, false); + } + + const fetch = cb => this.kraken.api('CancelOrder', reqData, this.handleResponse('cancelOrder', cb, false, order)); + retry(null, fetch, handle); +}; + + +Trader.prototype.getRawOpenOrders = function(callback) { + const handle = (err, data) => { + if(err) { + return callback(err) + } + + callback(undefined, data.result.open); + } + + const fetch = cb => this.kraken.api('OpenOrders', {}, this.handleResponse('getOpenOrders', cb, true)); + retry(null, fetch, handle); +} + +Trader.prototype.getOpenOrders = function(callback) { + + this.getRawOpenOrders((err, allOrders) => { + if(err) { + console.log(err); + + return callback(err) + } + + const orders = []; + + _.each(allOrders, (o, id) => { + if(o.descr.pair === this.pair) { + orders.push(id); + } + }); + + callback(undefined, orders); + }); +} + +Trader.getCapabilities = function () { + return { + name: 'Kraken', + slug: 'kraken', + currencies: marketData.currencies, + assets: marketData.assets, + markets: marketData.markets, + requires: ['key', 'secret'], + providesHistory: 'date', + providesFullHistory: true, + tid: 'date', + tradable: true, + gekkoBroker: 0.6 + }; +} + +module.exports = Trader; diff --git a/exchange/wrappers/lakebtc.js b/exchange/wrappers/lakebtc.js new file mode 100755 index 0000000..d80663f --- /dev/null +++ b/exchange/wrappers/lakebtc.js @@ -0,0 +1,138 @@ +var Lakebtc = require("lakebtc_nodejs"); +var util = require('../core/util.js'); +var _ = require('lodash'); +var moment = require('moment'); +var log = require('../core/log'); + +var Trader = function(config) { + _.bindAll(this); + if(_.isObject(config)) { + this.key = config.key; + this.secret = config.secret; + this.clientID = config.username; + } + this.name = 'LakeBTC'; + this.balance; + this.price; + + this.lakebtc = new Lakebtc(this.key, this.secret); +} + +// if the exchange errors we try the same call again after +// waiting 10 seconds +Trader.prototype.retry = function(method, args) { + var wait = +moment.duration(10, 'seconds'); + log.debug(this.name, 'returned an error, retrying..'); + + var self = this; + + // make sure the callback (and any other fn) + // is bound to Trader + _.each(args, function(arg, i) { + if(_.isFunction(arg)) + args[i] = _.bind(arg, self); + }); + + // run the failed method again with the same + // arguments after wait + setTimeout( + function() { method.apply(self, args) }, + wait + ); +} + +Trader.prototype.getPortfolio = function(callback) { + var set = function(err, data) { + var portfolio = []; + _.map(data.balance, function(amount, asset) { + portfolio.push({name: asset, amount: parseFloat(amount)}); + }); + callback(err, portfolio); + } + this.lakebtc.getAccountInfo(_.bind(set, this)); +} + +Trader.prototype.getTicker = function(callback) { + this.lakebtc.ticker(callback); +} + +Trader.prototype.getFee = function(callback) { + callback(false, 0.002); +} + +Trader.prototype.buy = function(amount, price, callback) { + var set = function(err, result) { + if(err || result.error) + return log.error('unable to buy:', err, result); + + callback(null, result.id); + }; + + // TODO: fees are hardcoded here? + amount *= 0.998; // remove fees + // prevent: Ensure that there are no more than 4 digits in total. + amount *= 10000; + amount = Math.floor(amount); + amount /= 10000; + this.lakebtc.buyOrder(_.bind(set, this), [price, amount, 'USD']); +} + +Trader.prototype.sell = function(amount, price, callback) { + var set = function(err, result) { + if(err || result.error) + return log.error('unable to sell:', err, result); + + callback(null, result.id); + }; + + this.lakebtc.sell(_.bind(set, this), [price, amount, 'USD']); +} + +Trader.prototype.checkOrder = function(order, callback) { + var check = function(err, result) { + var stillThere = _.find(result, function(o) { return o.id === order }); + callback(err, !stillThere); + }; + + this.lakebtc.getOrders(_.bind(check, this)); +} + +Trader.prototype.cancelOrder = function(order, callback) { + var cancel = function(err, result) { + if(err || !result.result) + log.error('unable to cancel order', order, '(', err, result, ')'); + }; + + this.lakebtc.cancelOrder(_.bind(cancel, this), [order]); +} + +Trader.prototype.getTrades = function(since, callback, descending) { + var args = _.toArray(arguments); + var process = function(err, result) { + if(err) + return this.retry(this.getTrades, args); + callback(null, descending ? result.reverse() : result); + }; + since = since ? since.unix() : moment().subtract(5, 'minutes').unix(); + this.lakebtc.bctrades( _.bind(process, this), since); +} + +Trader.getCapabilities = function () { + return { + name: 'LakeBTC', + slug: 'lakebtc', + currencies: ['USD'], + assets: ['BTC'], + markets: [ + { + pair: ['USD', 'BTC'], minimalOrder: { amount: 1, unit: 'currency' } + } + ], + requires: ['key', 'secret'], + providesHistory: false, + fetchTimespan: 60, + tid: 'tid' + }; +} + +module.exports = Trader; diff --git a/exchange/wrappers/luno.js b/exchange/wrappers/luno.js new file mode 100644 index 0000000..c3f697f --- /dev/null +++ b/exchange/wrappers/luno.js @@ -0,0 +1,416 @@ +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; + this.secret = config.secret; + this.currency = config.currency; + this.asset = config.asset; + this.pair = config.asset + config.currency; + } + 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 = 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); + amount *= precision; + amount = Math.floor(amount); + amount /= precision; + return amount; +} + +const round = (value, decimals = 14) => { + return Number(Math.round(value + 'e' + decimals) + 'e-' + decimals); +} + +const includes = (str, list) => { + if (!_.isString(str)) + return false; + let result = _.some(list, item => str.includes(item)); + return result; +} + +const recoverableErrors = [ + 'SOCKETTIMEDOUT', + 'TIMEDOUT', + 'CONNRESET', + 'CONNREFUSED', + '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('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 = 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); + } + + return callback(undefined, body); + } +}; + +//------- Gekko Functions ---------// + +Trader.prototype.getTicker = function(callback) { + // log(name, 'getTicker()'); + + const process = (err, data) => { + if (err) { + log(name, 'Error: -->', err); + return callback(err); + } + const ticker = { + ask: data.ask, + bid: data.bid, + spread: round(data.ask - data.bid) + }; + log(name, 'getTicker() -->', this.inspect(ticker)); + callback(undefined, ticker); + }; + + const handler = cb => this.luno.getTicker(processResponse('getTicker', cb)); + retry(null, handler, process); +} + +Trader.prototype.getFee = function(callback) { + // log(name, 'getFee()'); + + if (this.pair === 'ETHXBT') + return callback(undefined, 0.000025); + else if (this.pair === 'XBTIDR') + return callback(undefined, 0.00002); + else + 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) { + 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); + + if (!_.isNumber(assetAmount) || _.isNaN(assetAmount)) { + assetAmount = 0; + } + + if (!_.isNumber(currencyAmount) || _.isNaN(currencyAmount)) { + currencyAmount = 0; + } + + const portfolio = [{ + name: this.asset.toUpperCase(), + amount: assetAmount + }, + { + name: this.currency.toUpperCase(), + amount: currencyAmount + } + ]; + log(name, 'getPortfolio() --> ' + this.inspect(portfolio)); + callback(undefined, portfolio); + }; + + const handler = cb => this.luno.getBalance(processResponse('getPortfolio', cb)); + retry(null, handler, process); +} + +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) { + log(name, 'unable to buy:', err.message); + return callback(err); + } + log(name, 'buy() order id: -->', data.order_id); + callback(undefined, data.order_id); + }; + + const handler = cb => this.luno.postBuyOrder(amount, price, processResponse('buy', cb)); + retry(null, handler, process); +} + +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) { + log(name, 'unable to sell:', err.message); + return callback(err); + } + log(name, 'sell() order id: -->', data.order_id); + callback(undefined, data.order_id); + }; + + const handler = cb => this.luno.postSellOrder(amount, price, processResponse('sell', cb)); + retry(null, handler, process); +} + +Trader.prototype.roundAmount = function(amount) { + amount = setPrecision(amount, this.market.precision); + return amount; +} + +Trader.prototype.roundPrice = function(price) { + return +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) { + log(name, 'Error: -->', err); + return callback(err); + } + const price = parseFloat(data.limit_price); + const amount = parseFloat(data.base); + let date = moment(); + 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); + } + 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)); + retry(null, handler, process); +} + +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) { + 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); + }; + + const handler = cb => this.luno.getOrder(order, processResponse('checkOrder', cb)); + retry(null, handler, process); +} + +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')) { + log(name, 'unable to cancel order:', order, '(' + err.message + ') assuming success...'); + } else { + log(name, 'unable to cancel order:', order, '(' + err.message + ') aborting...'); + return callback(err); + } + } + + if (data && !data.success) { + log('cancelOrder() --> status:', data.success); + return callback(undefined, false); + } + + this.checkOrder(order, (error, orderStatus) => { + if (error) { + 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); + } + 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)); + retry(null, handler, process); +} + +Trader.prototype.getTrades = function(since, callback, descending) { + // log(name, 'getTrades() since:', since); + + const process = (err, result) => { + if (err) { + log(name, 'Error: -->', err); + return callback(err); + } + let trades = _.map(result.trades, (t) => { + return { + price: t.price, + date: Math.round(t.timestamp / 1000), + amount: t.volume, + tid: t.timestamp + } + }); + if (!descending) { + trades = trades.reverse() + } + callback(undefined, trades); + }; + + if (moment.isMoment(since)) since = since.valueOf(); + (_.isNumber(since) && since > 0) ? since: since = 0; + + const options = { + pair: this.pair, + since: since + } + + const handler = cb => this.luno.getTrades(options, processResponse('getTrades', cb)); + retry(null, handler, process); +} + +Trader.getCapabilities = function() { + return { + name: 'Luno', + slug: 'luno', + currencies: ['MYR', 'KES', 'NGN', 'ZAR', 'XBT'], + assets: ['ETH', 'XBT'], + markets: [ + { pair: ['XBT', 'ETH'], minimalOrder: { amount: 0.01, unit: 'asset' }, precision: 2 }, + { pair: ['MYR', 'XBT'], minimalOrder: { amount: 0.0005, unit: 'asset' }, precision: 6 }, + { 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', + // maxHistoryFetch: 100, + tid: 'tid', + tradable: true, + forceReorderDelay: true, + gekkoBroker: 0.6 + }; +} + +module.exports = Trader; diff --git a/exchange/wrappers/mexbt.js.old b/exchange/wrappers/mexbt.js.old new file mode 100644 index 0000000..af386f3 --- /dev/null +++ b/exchange/wrappers/mexbt.js.old @@ -0,0 +1,154 @@ +var Mexbt = require("mexbt"); +var util = require('../core/util.js'); +var _ = require('lodash'); +var moment = require('moment'); +var log = require('../core/log'); + +var Trader = function(config) { + _.bindAll(this); + if(_.isObject(config)) { + this.key = config.key; + this.secret = config.secret; + this.userId = config.username; + this.pair = config.asset || this.currency; + } + this.name = 'meXBT'; + this.mexbt = new Mexbt(this.key, this.secret, this.userId); +} + +// if the exchange errors we try the same call again after +// waiting 10 seconds +Trader.prototype.retry = function(method, args) { + var wait = +moment.duration(10, 'seconds'); + log.debug(this.name, 'returned an error, retrying..'); + + var self = this; + + // make sure the callback (and any other fn) + // is bound to Trader + _.each(args, function(arg, i) { + if(_.isFunction(arg)) + args[i] = _.bind(arg, self); + }); + + // run the failed method again with the same + // arguments after wait + setTimeout( + function() { method.apply(self, args) }, + wait + ); +} + +Trader.prototype.getPortfolio = function(callback) { + var set = function(err, data) { + var portfolio = []; + _.each(data.currencies, function(balanceInfo) { + portfolio.push({name: balanceInfo.name, amount: balanceInfo.balance}); + }); + callback(err, portfolio); + } + this.mexbt.accountBalance(_.bind(set, this)); +} + +Trader.prototype.getTicker = function(callback) { + this.mexbt.ticker(callback); +} + +Trader.prototype.getFee = function(callback) { + var set = function(err, data) { + if(err) + callback(err); + + callback(false, data.fee); + } + this.mexbt.getTradingFee({amount: 1, type: 'limit'}, _.bind(set, this)); +} + +Trader.prototype.buy = function(amount, price, callback) { + var set = function(err, result) { + if(err || result.error) + return log.error('unable to buy:', err, result); + + callback(null, result.serverOrderId); + }; + + this.mexbt.createOrder({amount: amount, price: price, side: 'buy', type: 'limit'}, _.bind(set, this)); +} + +Trader.prototype.sell = function(amount, price, callback) { + var set = function(err, result) { + if(err || result.error) + return log.error('unable to sell:', err, result); + + callback(null, result.serverOrderId); + }; + + this.mexbt.createOrder({amount: amount, price: price, side: 'sell', type: 'limit'}, _.bind(set, this)); +} + +Trader.prototype.checkOrder = function(order, callback) { + var currentPair = this.pair; + + var check = function(err, result) { + var ordersForPair = _.find(result, function(o) { return o.ins === currentPair}); + var stillThere = _.find(ordersForPair.openOrders, function(o) { return o.ServerOrderId === order }); + callback(err, !stillThere); + }; + + this.mexbt.accountOrders(_.bind(check, this)); +} + +Trader.prototype.cancelOrder = function(order, callback) { + var cancel = function(err, result) { + if(err || !result) + log.error('unable to cancel order', order, '(', err, result, ')'); + }; + + this.mexbt.cancelOrder({id: order}, _.bind(cancel, this)); +} + +Trader.prototype.getTrades = function(since, callback, descending) { + var args = _.toArray(arguments); + var process = function(err, result) { + if(err) + return this.retry(this.getTrades, args); + trades = _.map(result.trades, function (t) { + return {tid: t.tid, price: t.px, date: t.unixtime, amount: t.qty}; + }); + if (descending) { + trades = trades.reverse() + } + callback(null, trades); + }.bind(this); + + var endDate = moment().unix(); + + // FIXME: there is a bug in meXBT tradesByDate function, that it doesnt return all data + // when trying to fetch all. + // So if no since, we just fetch all via trades and giving a high count + if (since) { + this.mexbt.tradesByDate({startDate: since.unix(), endDate: endDate}, process); + } else { + // improvised + this.mexbt.trades({count: 1000}, process); + } +} + +Trader.getCapabilities = function () { + return { + name: 'meXBT', + slug: 'mexbt', + currencies: ['MXN'], + assets: ['BTC'], + markets: [ + { + pair: ['MXN', 'BTC'], minimalOrder: { amount: 0.01, unit: 'asset' } + } + ], + requires: ['key', 'secret', 'username'], + providesHistory: 'date', + tid: 'tid' + }; +} + +module.exports = Trader; diff --git a/exchange/wrappers/mtgox.js.old b/exchange/wrappers/mtgox.js.old new file mode 100644 index 0000000..60989c8 --- /dev/null +++ b/exchange/wrappers/mtgox.js.old @@ -0,0 +1,214 @@ +// HERE FOR HISTORICAL PURPOSES + +var MtGoxClient = require("mtgox-apiv2"); +var _ = require('lodash'); +var moment = require('moment'); +var util = require('../core/util.js'); +var log = require('../core/log'); + +var Trader = function(config) { + if(_.isObject(config)) { + this.key = config.key; + this.secret = config.secret; + this.currency = config.currency || 'USD'; + + this.pair = 'BTC' + this.currency; + } + this.name = 'Mt. Gox'; + + _.bindAll(this); + + this.mtgox = new MtGoxClient(this.key, this.secret, this.pair); +} + +Trader.prototype.buy = function(amount, price, callback) { + var process = function(err, result) { + this.checkUnauthorized(err); + // if Mt. Gox is down or lagging + if(err || result.result === 'error') + log.error('unable to buy (', err, result, ')'); + + callback(null, result.data); + }; + this.mtgox.add('bid', amount, price, _.bind(process, this)); +} + +Trader.prototype.sell = function(amount, price, callback) { + var process = function(err, result) { + this.checkUnauthorized(err); + // if Mt. Gox is down or lagging + if(err || result.result === 'error') + log.error('unable to sell (', err, result, ')'); + + callback(null, result.data); + }; + this.mtgox.add('ask', amount, price, _.bind(process, this)); +} + +Trader.prototype.getTrades = function(since, callback, descending) { + if(since && !_.isNumber(since)) + since = util.toMicro(since); + + var args = _.toArray(arguments); + this.mtgox.fetchTrades(since, _.bind(function(err, trades) { + if (err || !trades) + return this.retry(this.getTrades, args); + + trades = trades.data; + if (trades.length === 0) + return this.retry(this.getTrades, args); + + if(descending) + callback(false, trades.reverse()); + else + callback(false, trades); + }, this)); +} + +// if the exchange errors we try the same call again after +// waiting 10 seconds +Trader.prototype.retry = function(method, args) { + var wait = +moment.duration(10, 'seconds'); + log.debug(this.name, 'returned an error, retrying..'); + + var self = this; + + // make sure the callback (and any other fn) + // is bound to Trader + _.each(args, function(arg, i) { + if(_.isFunction(arg)) + args[i] = _.bind(arg, self); + }); + + // run the failed method again with the same + // arguments after wait. + setTimeout( + function() { method.apply(self, args) }, + wait + ); +} + +Trader.prototype.checkUnauthorized = function(err) { + if(err && err.message === 'Request failed with 403') + util.die('It appears your ' + this.name + ' API key and secret are incorrect'); +} + +// calls callback with the following data structure: +// +// [ +// { name: 'BTC', amount: 10.12413123}, +// { name: 'USD', amount: 500.0241}, +// { name: 'EUR', amount: 0}, +// ] +Trader.prototype.getPortfolio = function(callback) { + var args = _.toArray(arguments); + var calculate = function(err, result) { + this.checkUnauthorized(err); + if(err) + return this.retry(this.getPortfolio, args); + + if(!('Wallets' in result.data)) + log.error('unable to get portfolio, do I have get_info rights?'); + + var assets = []; + _.each(result.data.Wallets, function(wallet, name) { + var amount = parseFloat(wallet.Balance.value); + assets.push({name: name, amount: amount}); + }); + callback(null, assets); + }; + + this.mtgox.info(_.bind(calculate, this)); +} + +Trader.prototype.getFee = function(callback) { + var args = _.toArray(arguments); + var calculate = function(err, result) { + this.checkUnauthorized(err); + if(err) + return this.retry(this.getFee, args); + + // convert the % + var fee = result.data.Trade_Fee / 100; + callback(null, fee); + }; + + this.mtgox.info(_.bind(calculate, this)); +} + +Trader.prototype.checkOrder = function(order, callback) { + var args = _.toArray(arguments); + // we can't just request the order id + // @link https://bitbucket.org/nitrous/mtgox-api/#markdown-header-moneyorderresult + var check = function(err, result) { + if(err) + return this.retry(this.checkOrder, args); + + var stillThere = _.find(result.data, function(o) { return o.oid === order }); + callback(null, !stillThere); + }; + + this.mtgox.orders(_.bind(check, this)); +} + +Trader.prototype.cancelOrder = function(order) { + var cancel = function(err, result) { + if(err || result.result !== 'succes') + log.error('unable to cancel order', order, '(', err, result, ')'); + }; + + this.mtgox.cancel(order, _.bind(cancel, this)); +} + +Trader.prototype.getTicker = function(callback) { + var args = _.toArray(arguments); + var set = function(err, result) { + if(err) + return this.retry(this.getTicker, args); + + var ticker = { + bid: result.data.buy.value, + ask: result.data.sell.value + } + callback(err, ticker); + }; + + this.mtgox.ticker(_.bind(set, this)); +} + +Trader.getCapabilities = function () { + return {}; + // ---- Keeping this here for historical purposes. ---- + // { + // + // name: 'MtGox', + // slug: 'mtgox', + // direct: true, + // infinityOrder: true, + // currencies: [ + // 'USD', 'EUR', 'GBP', 'AUD', 'CAD', 'CHF', 'CNY', + // 'DKK', 'HKD', 'PLN', 'RUB', 'SGD', 'THB' + // ], + // assets: ['BTC'], + // markets: [ + // { pair: ['USD', 'BTC'], minimalOrder: { amount: 0.01, unit: 'asset' } }, + // { pair: ['EUR', 'BTC'], minimalOrder: { amount: 0.01, unit: 'asset' } }, + // { pair: ['GBP', 'BTC'], minimalOrder: { amount: 0.01, unit: 'asset' } }, + // { pair: ['AUD', 'BTC'], minimalOrder: { amount: 0.01, unit: 'asset' } }, + // { pair: ['CAD', 'BTC'], minimalOrder: { amount: 0.01, unit: 'asset' } }, + // { pair: ['CHF', 'BTC'], minimalOrder: { amount: 0.01, unit: 'asset' } }, + // { pair: ['CNY', 'BTC'], minimalOrder: { amount: 0.01, unit: 'asset' } }, + // { pair: ['DKK', 'BTC'], minimalOrder: { amount: 0.01, unit: 'asset' } }, + // { pair: ['HKD', 'BTC'], minimalOrder: { amount: 0.01, unit: 'asset' } }, + // { pair: ['PLN', 'BTC'], minimalOrder: { amount: 0.01, unit: 'asset' } }, + // { pair: ['RUB', 'BTC'], minimalOrder: { amount: 0.01, unit: 'asset' } }, + // { pair: ['SGD', 'BTC'], minimalOrder: { amount: 0.01, unit: 'asset' } }, + // { pair: ['THB', 'BTC'], minimalOrder: { amount: 0.01, unit: 'asset' } } + // ], + // requires: ['key', 'secret'], + // providesHistory: false + // } +} + +module.exports = Trader; + diff --git a/exchange/wrappers/okcoin.js.old b/exchange/wrappers/okcoin.js.old new file mode 100644 index 0000000..1134697 --- /dev/null +++ b/exchange/wrappers/okcoin.js.old @@ -0,0 +1,194 @@ +var OKCoin = require('okcoin-china'); +var util = require('../core/util.js'); +var _ = require('lodash'); +var moment = require('moment'); +var log = require('../core/log'); + +var Trader = function(config) { + _.bindAll(this); + if(_.isObject(config)) { + this.key = config.key; + this.secret = config.secret; + this.clientID = config.username; + } + this.pair = [config.asset, config.currency].join('_').toLowerCase(); + this.name = 'okcoin'; + this.okcoin = new OKCoin(this.key, this.secret); + this.lastTid = false; +} + +// if the exchange errors we try the same call again after +// waiting 10 seconds +Trader.prototype.retry = function(method, args) { + var wait = +moment.duration(10, 'seconds'); + log.debug(this.name, 'returned an error, retrying..'); + + var self = this; + + // make sure the callback (and any other fn) + // is bound to Trader + _.each(args, function(arg, i) { + if (_.isFunction(arg)) + args[i] = _.bind(arg, self); + }); + + // run the failed method again with the same + // arguments after wait + setTimeout( + function() { + method.apply(self, args) + }, + wait + ); +} + +Trader.prototype.getPortfolio = function(callback) { + var calculate = function(err, data) { + if(err) { + if(err.message === 'invalid api key') + util.die('Your ' + this.name + ' API keys are invalid'); + return this.retry(this.okcoin.getUserInfo, calculate); + } + + var portfolio = []; + _.each(data.info.funds.free, function(amount, asset) { + portfolio.push({name: asset.toUpperCase(), amount: +amount}); + }); + + callback(err, portfolio); + }.bind(this); + + this.okcoin.getUserInfo(calculate); +} + +Trader.prototype.getTicker = function(callback) { + var args = [this.pair, process]; + var process = function(err, data) { + if (err) + return this.retry(this.okcoin.getTicker(args)); + + var ticker = _.extend(data.ticker, { + bid: +data.ticker.sell, + ask: +data.ticker.buy + }); + + callback(err, ticker); + }.bind(this); + + this.okcoin.getTicker(process, args); +} + +// This assumes that only limit orders are being placed, so fees are the +// "maker fee" of 0.1%. It does not take into account volume discounts. +Trader.prototype.getFee = function(callback) { + var makerFee = 0.1; + callback(false, makerFee / 100); +} + +Trader.prototype.buy = function(raw_amount, price, callback) { + var amount = Math.floor(raw_amount * 10000) / 10000 + var set = function(err, result) { + if(err) + return log.error('unable to process order:', err, result); + + callback(null, result.order_id); + }.bind(this); + + this.okcoin.addTrade(set, this.pair, 'buy', amount, price); +} + +Trader.prototype.sell = function(raw_amount, price, callback) { + var amount = Math.floor(raw_amount * 10000) / 10000 + var set = function(err, result) { + if(err) + return log.error('unable to process order:', err, result); + + callback(null, result.order_id); + }.bind(this); + + this.okcoin.addTrade(set, this.pair, 'sell', amount, price); +} + + +Trader.prototype.checkOrder = function(order_id, callback) { + var check = function(err, result) { + if(err || !result.result) { + log.error('Perhaps the order already got filled?', '(', result, ')'); + callback(err, !result.result); + } else { + callback(err, true); + } + } + + this.okcoin.getOrderInfo(check, this.pair, order_id); +} + +Trader.prototype.cancelOrder = function(order_id, callback) { + var cancel = function(err, result) { + if(err || !result.result) { + return log.error('unable to cancel order ', order_id, '(', result, ')'); + } + + callback(); + }.bind(this); + + this.okcoin.cancelOrder(cancel, this.pair, order_id); +} + +Trader.prototype.getOrder = function(order_id, callback) { + + var handle = (err, resp) => { + var order = _.first(resp.orders); + + var amount = order.deal_amount; + var price = order.price; + var date = moment(order.date).utc(); + + callback(undefined, {amount, price, date}); + } + + this.okcoin.getOrderInfo(handle, this.pair, order_id) +} + +Trader.prototype.getTrades = function(since, callback, descending) { + var args = _.toArray(arguments); + + if(since) + since = 600; + + this.okcoin.getTrades(function(err, data) { + if (err) + return this.retry(this.getTrades, args); + + var trades = _.map(data, function(trade) { + return { + price: +trade.price, + amount: +trade.amount, + tid: +trade.tid, + date: trade.date + } + }); + + callback(null, trades.reverse()); + }.bind(this), this.pair, since); +} + +Trader.getCapabilities = function () { + return { + name: 'OkCoin', + slug: 'okcoin', + currencies: ['BTC', 'CNY'], + assets: ['BTC', 'LTC'], + markets: [ + { pair: ['CNY', 'BTC'], minimalOrder: { amount: 0.01, unit: 'asset' } }, + { pair: ['CNY', 'LTC'], minimalOrder: { amount: 0.01, unit: 'asset' } } + ], + requires: ['key', 'secret', 'username'], + providesHistory: false, + fetchTimespan: 60, + tid: 'date', + tradable: true + }; +} + +module.exports = Trader; \ No newline at end of file diff --git a/exchange/wrappers/poloniex-markets.json b/exchange/wrappers/poloniex-markets.json new file mode 100644 index 0000000..687162a --- /dev/null +++ b/exchange/wrappers/poloniex-markets.json @@ -0,0 +1,3257 @@ +{ + "currencies": ["BTC", "ETH", "XMR", "USDT", "USDC"], + "assets": [ + "1CR", "ABY", "AC", "ACH", "ADN", "AEON", "AERO", "AIR", "AMP", "APH", + "ARCH", "AUR", "AXIS", "BALLS", "BANK", "BBL", "BBR", "BCC", "BCH", "BCN", + "BCY", "BDC", "BDG", "BELA", "BITCNY", "BITS", "BITUSD", "BLK", "BLOCK", + "BLU", "BNS", "BONES", "BOST", "BTC", "BTCD", "BTCS", "BTM", "BTS", + "BURN", "BURST", "C2", "CACH", "CAI", "CC", "CCN", "CGA", "CHA", "CINNI", + "CLAM", "CNL", "CNMT", "CNOTE", "COMM", "CON", "CORG", "CRYPT", "CURE", + "CVC", "CYC", "DAO", "DASH", "DCR", "DGB", "DICE", "DIEM", "DIME", "DIS", "DNS", + "DOGE", "DRKC", "DRM", "DSH", "DVK", "EAC", "EBT", "ECC", "EFL", "EMC2", + "EMO", "ENC", "ETC", "ETH", "eTOK", "EXE", "EXP", "FAC", "FCN", "FCT", + "FIBRE", "FLAP", "FLDC", "FLO", "FLT", "FOX", "FRAC", "FRK", "FRQ", "GNO", + "FVZ", "FZ", "FZN", "GAME", "GAP", "GAS", "GDN", "GEMZ", "GEO", "GIAR", "GLB", + "GML", "GNS", "GNT", "GOLD", "GPC", "GPUC", "GRC", "GRCX", "GRS", "GUE", "H2O", + "HIRO", "HOT", "HUC", "HUGE", "HVC", "HYP", "HZ", "IFC", "INDEX", "IOC", + "ITC", "IXC", "JLH", "JPC", "JUG", "KDC", "KEY", "LBC", "LC", "LCL", + "LEAF", "LGC", "LOL", "LOVE", "LQD", "LSK", "LTBC", "LTC", "LTCX", + "MAID", "MAST", "MAX", "MCN", "MEC", "METH", "MIL", "MIN", "MINT", "MMC", + "MMNXT", "MMXIV", "MNTA", "MON", "MRC", "MRS", "MTS", "MUN", "MYR", + "MZC", "N5X", "NAS", "NAUT", "NAV", "NBT", "NEOS", "NL", "NMC", "NOBL", + "NOTE", "NOXT", "NRS", "NSR", "NTX", "NXT", "NXTI", "OMG", "OMNI", "OPAL", + "PAND", "PASC", "PAWN", "PIGGY", "PINK", "PLX", "PMC", "POT", "PPC", "PRC", + "PRT", "PTS", "Q2C", "QBK", "QCN", "QORA", "QTL", "RADS", "RBY", "RDD", "REP", + "RIC", "RZR", "SBD", "SC", "SDC", "SHIBE", "SHOPX", "SILK", "SJCX", + "SLR", "SMC", "SOC", "SPA", "SQL", "SRCC", "SRG", "SSD", "STEEM", "STR", + "SUM", "SUN", "SWARM", "SXC", "SYNC", "SYS", "TAC", "TOR", "TRUST", + "TWE", "UIS", "ULTC", "UNITY", "URO", "USDE", "USDT", "UTC", "UTIL", + "UVC", "VIA", "VOOT", "VOX", "VRC", "VTC", "WC", "WDC", "WIKI", "WOLF", + "X13", "XAI", "XAP", "XBC", "XC", "XCH", "XCN", "XCP", "XCR", "XDN", + "XDP", "XEM", "XHC", "XLB", "XMG", "XMR", "XPB", "XPM", "XRP", "XSI", + "XST", "XSV", "XUSD", "XVC", "XXC", "BCH", "YACC", "YANG", "YC", "YIN", "ZEC", + "ZRX", "BCHSV", "BCHABC" + ], + "markets": [ + { + "pair": [ + "BTC", + "1CR" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "ABY" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "AC" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "ACH" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "ADN" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "AEON" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "AERO" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "AIR" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "AMP" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "APH" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "ARCH" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "AUR" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "AXIS" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "BALLS" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "BANK" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "BBL" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "BBR" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "BCC" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "BCN" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "BCH" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "BCY" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "BDC" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "BDG" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "BELA" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "BITCNY" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "BITS" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "BITUSD" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "BLK" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "BLOCK" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "BLU" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "BNS" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "BONES" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "BOST" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "BTCD" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "BTCS" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "BTM" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "BTS" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "BURN" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "BURST" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "C2" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "CACH" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "CAI" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "CC" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "CCN" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "CGA" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "CHA" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "CINNI" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "CLAM" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "CNL" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "CNMT" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "CNOTE" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "COMM" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "CON" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "CORG" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "CRYPT" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "CURE" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "CVC" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "CYC" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "DAO" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "DASH" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "DCR" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "DGB" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "DICE" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "DIEM" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "DIME" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "DIS" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "DNS" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "DOGE" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "DRKC" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "DRM" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "DSH" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "DVK" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "EAC" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "EBT" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "ECC" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "EFL" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "EMC2" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "EMO" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "ENC" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "ETC" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "ETH" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "eTOK" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "EXE" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "EXP" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "FAC" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "FCN" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "FCT" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "FIBRE" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "FLAP" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "FLDC" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "FLO" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "FLT" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "FOX" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "FRAC" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "FRK" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "FRQ" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "FVZ" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "FZ" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "FZN" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "GAME" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "GAP" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "GAS" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "GDN" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "GEMZ" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "GEO" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "GIAR" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "GLB" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "GML" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "GNS" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "GOLD" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "GPC" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "GPUC" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "GRC" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "GRCX" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "GRS" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "GUE" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "H2O" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "HIRO" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "HOT" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "HUC" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "HUGE" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "HVC" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "HYP" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "HZ" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "IFC" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "INDEX" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "IOC" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "ITC" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "IXC" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "JLH" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "JPC" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "JUG" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "KDC" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "KEY" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "LBC" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "LC" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "LCL" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "LEAF" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "LGC" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "LOL" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "LOVE" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "LQD" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "LSK" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "LTBC" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "LTC" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "LTCX" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "MAID" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "MAST" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "MAX" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "MCN" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "MEC" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "METH" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "MIL" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "MIN" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "MINT" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "MMC" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "MMNXT" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "MMXIV" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "MNTA" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "MON" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "MRC" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "MRS" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "MTS" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "MUN" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "MYR" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "MZC" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "N5X" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "NAS" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "NAUT" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "NAV" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "NBT" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "NEOS" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "NL" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "NMC" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "NOBL" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "NOTE" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "NOXT" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "NRS" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "NSR" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "NTX" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "NXT" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "NXTI" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "OMG" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "OMNI" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "OPAL" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "PAND" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "PASC" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "PAWN" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "PIGGY" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "PINK" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "PLX" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "PMC" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "POT" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "PPC" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "PRC" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "PRT" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "PTS" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "Q2C" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "QBK" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "QCN" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "QORA" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "QTL" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "RADS" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "RBY" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "RDD" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "REP" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "RIC" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "RZR" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "SBD" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "SC" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "SDC" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "SHIBE" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "SHOPX" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "SILK" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "SJCX" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "SLR" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "SMC" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "SOC" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "SPA" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "SQL" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "SRCC" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "SRG" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "SSD" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "STEEM" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "STR" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "SUM" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "SUN" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "SWARM" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "SXC" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "SYNC" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "SYS" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "TAC" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "TOR" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "TRUST" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "TWE" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "UIS" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "ULTC" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "UNITY" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "URO" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "USDE" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "USDT" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "UTC" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "UTIL" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "UVC" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "VIA" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "VOOT" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "VOX" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "VRC" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "VTC" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "WC" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "WDC" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "WIKI" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "WOLF" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "X13" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "XAI" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "XAP" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "XBC" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "XC" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "XCH" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "XCN" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "XCP" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "XCR" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "XDN" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "XDP" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "XEM" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "XHC" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "XLB" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "XMG" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "XMR" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "XPB" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "XPM" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "XRP" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "XSI" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "XST" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "XSV" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "XUSD" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "XVC" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "XXC" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "YACC" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "YANG" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "YC" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "YIN" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "ZEC" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "ZRX" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "USDT", + "BTC" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "USDT", + "BCH" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "USDT", + "DASH" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "USDT", + "ETC" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "USDT", + "ETH" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "USDT", + "LTC" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "USDT", + "NXT" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "USDT", + "REP" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "USDT", + "STR" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "USDT", + "XMR" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "USDT", + "XRP" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "USDT", + "ZEC" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "ETH", + "CVC" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "ETH", + "ETC" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "ETH", + "BCH" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "ETH", + "GAS" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "ETH", + "GNO" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "ETH", + "GNT" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "ETH", + "LSK" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "ETH", + "OMG" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "ETH", + "REP" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "ETH", + "STEEM" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "ETH", + "ZEC" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "ETH", + "ZRX" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "XMR", + "BCN" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "XMR", + "BLK" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "XMR", + "BTCD" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "XMR", + "DASH" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "XMR", + "LTC" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "XMR", + "MAID" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "XMR", + "NXT" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "XMR", + "ZEC" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "USDC", + "BTC" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "USDC", + "USDT" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "USDC", + "ETH" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "USDC", + "BCHSV" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "USDC", + "BCHABC" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "USDC", + "BCH" + ], + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + } + ] +} \ No newline at end of file diff --git a/exchange/wrappers/poloniex.js b/exchange/wrappers/poloniex.js new file mode 100644 index 0000000..f91b6fa --- /dev/null +++ b/exchange/wrappers/poloniex.js @@ -0,0 +1,533 @@ +const Poloniex = require("gekko-broker-poloniex"); +const _ = require('lodash'); +const moment = require('moment'); +const retry = require('../exchangeUtils').retry; +const marketData = require('./poloniex-markets.json'); + +const Trader = function(config) { + _.bindAll(this); + if(_.isObject(config)) { + this.key = config.key; + this.secret = config.secret; + this.currency = config.currency; + this.asset = config.asset; + } + this.name = 'Poloniex'; + this.balance; + this.price; + + this.pair = this.currency + '_' + this.asset; + + this.fillDataOnCancel = true; + + this.poloniex = new Poloniex({ + key: this.key, + secret: this.secret, + userAgent: 'Gekko Broker v' + require('../package.json').version + }); +} + +const recoverableErrors = [ + 'SOCKETTIMEDOUT', + 'TIMEDOUT', + 'CONNRESET', + 'CONNREFUSED', + 'NOTFOUND', + '429', + '522', + '504', + '503', + '500', + '502', + 'socket hang up', + 'Empty response', + 'Please try again in a few minutes.', + 'Nonce must be greater than', + 'Internal error. Please try again.', + 'Connection timed out. Please try again.', + // getaddrinfo EAI_AGAIN poloniex.com poloniex.com:443 + 'EAI_AGAIN', + 'ENETUNREACH', + 'socket hang up' +]; + +// errors that might mean +// the API call succeeded. +const unknownResultErrors = [ + 'ETIMEDOUT', +] + +const includes = (str, list) => { + if(!_.isString(str)) + return false; + + return _.some(list, item => str.includes(item)); +} + +Trader.prototype.outbidPrice = function(price, isUp) { + let newPrice; + + const tickSize = 0.00000001; + + if(isUp) { + newPrice = price + 0.00000001; + } else { + newPrice = price - tickSize; + } + + return this.roundPrice(newPrice); +} + +Trader.prototype.processResponse = function(next, fn, payload) { + // TODO: in very rare cases the callback is + // called twice (first on ETIMEDOUT later + // without error). Temp workaround. + next = _.once(next); + + return (err, data) => { + let error; + + if(err) { + if(err.message) { + error = err; + } else { + console.log('err but no message,', typeof err, {err, data}); + error = new Error(err); + } + } else if(!data) { + error = new Error('Empty response'); + } else if(data.error) { + error = new Error(data.error); + } else if(includes(data, ['Please complete the security check to proceed.'])) { + error = new Error( + 'Your IP has been flagged by CloudFlare. ' + + 'As such Gekko Broker cannot access Poloniex.' + ); + data = undefined; + } else if(includes(data, ['Please try again in a few minutes.'])) { + error = new Error('Please try again in a few minutes.'); + error.notFatal = true; + data = undefined; + } else if(includes(data, [''])) { + error = new Error(data); + data = undefined; + } + + if(error) { + + if(includes(error.message, recoverableErrors)) { + error.notFatal = true; + } + + if(includes(error.message, ['Currently in maintenance mode.'])) { + console.log(new Date, '[Poloniex] Currently in maintenance mode. Retrying...'); + error.notFatal = true; + error.backoffDelay = 1000; + } + + // not actually an error, means order never executed against other trades + if(fn === 'getOrder' && + error.message.includes('Order not found, or you are not the person who placed it.') + ) { + error = undefined; + data = { unfilled: true }; + console.log(new Date, 'UNKNOWN ORDER!', payload); + } + + if(fn === 'cancelOrder') { + + // already filled + if(includes(error.message, ['Invalid order number, or you are not the person who placed the order.'])) { + console.log(new Date, 'cancelOrder invalid order'); + error = undefined; + data = { filled: true }; + } + + // it might be cancelled + else if(includes(error.message, unknownResultErrors)) { + setTimeout(() => { + this.getRawOpenOrders((err, orders) => { + if(err) { + return next(err); + } + + const order = _.find(orders, o => o.orderNumber == payload); + + // the cancel did not work since the order still exists + if(order) { + error.notFatal = true; + return next(error); + } + + // it was cancelled, we need to check filled amount.. + console.log(new Date, '[CANCELFIX] process cancel response'); + console.log('[CANCELFIX] rechecking fill') + setTimeout(() => { + this.getOrder(payload, (error, order) => { + if(error) { + return next(error) + } + + console.log('[CANCELFIX] checked, got:', order); + + return next(undefined, { filled: order.amount }); + }); + }, this.checkInterval); + }); + + }, this.checkInterval); + return; + } + } + + if(fn === 'order') { + if(includes(error.message, ['Not enough'])) { + error.retry = 2; + } + + // we need to check whether the order was actually created + if(includes(error.message, unknownResultErrors)) { + return setTimeout(() => { + this.findLastOrder(10, payload, (err, lastTrade) => { + if(lastTrade) { + return next(undefined, lastTrade); + } + + next(error); + }); + + }, this.checkInterval); + } + } + } + + return next(error, data); + } +} + +Trader.prototype.findLastOrder = function(since, side, callback) { + const handle = (err, result) => { + if(err) { + return callback(err); + } + + result = result.filter(t => t.type === side); + + if(!result.length) { + return callback(undefined, undefined); + } + + let order; + if(since) { + const threshold = moment().subtract(since, 'm'); + order = _.find(result, o => moment.utc(o.date) > threshold); + } else { + order = _.last(result); + } + + callback(undefined, order); + }; + + this.getRawOpenOrders(handle); +} + +Trader.prototype.getRawOpenOrders = function(callback) { + const fetch = next => this.poloniex.returnOpenOrders(this.currency, this.asset, this.processResponse(next)); + retry(null, fetch, callback); +} + +Trader.prototype.getOpenOrders = function(callback) { + this.getRawOpenOrders((err, orders) => { + if(err) { + return callback(err); + } + + console.log(orders); + + const ids = orders.map(o => o.orderNumber); + + return callback(undefined, ids); + }) +} + +Trader.prototype.getPortfolio = function(callback) { + const handle = (err, data) => { + if(err) { + return callback(err); + } + + var assetAmount = parseFloat( data[this.asset] ); + var currencyAmount = parseFloat( data[this.currency] ); + + if( + !_.isNumber(assetAmount) || _.isNaN(assetAmount) || + !_.isNumber(currencyAmount) || _.isNaN(currencyAmount) + ) { + assetAmount = 0; + currencyAmount = 0; + } + + var portfolio = [ + { name: this.asset, amount: assetAmount }, + { name: this.currency, amount: currencyAmount } + ]; + + callback(undefined, portfolio); + } + + const fetch = next => this.poloniex.myBalances(this.processResponse(next)); + retry(null, fetch, handle); +} + +Trader.prototype.getFullPortfolio = function(callback) { + const handle = (err, data) => { + if(err) { + return callback(err); + } + + const asset = data[this.asset]; + const currency = data[this.currency]; + + let assetAmount = parseFloat(asset.available) + parseFloat(asset.onOrders); + let currencyAmount = parseFloat(currency.available) + parseFloat(asset.onOrders); + + if(!_.isNumber(assetAmount) || _.isNaN(assetAmount)) { + assetAmount = 0; + } + + if(!_.isNumber(currencyAmount) || _.isNaN(currencyAmount)) { + currencyAmount = 0; + } + + var portfolio = [ + { name: this.asset, amount: assetAmount }, + { name: this.currency, amount: currencyAmount } + ]; + + callback(undefined, portfolio); + } + + const fetch = next => this.poloniex.returnCompleteBalances(this.processResponse(next)); + retry(null, fetch, handle); +} + +Trader.prototype.getTicker = function(callback) { + const handle = (err, data) => { + if(err) + return callback(err); + + var tick = data[this.pair]; + + callback(null, { + bid: parseFloat(tick.highestBid), + ask: parseFloat(tick.lowestAsk), + }); + }; + + + const fetch = next => this.poloniex.getTicker(this.processResponse(next)); + retry(null, fetch, handle); +} + +Trader.prototype.getFee = function(callback) { + const handle = (err, data) => { + if(err) + return callback(err); + + callback(undefined, parseFloat(data.makerFee)); + } + + const fetch = next => this.poloniex._private('returnFeeInfo', this.processResponse(next)); + retry(null, fetch, handle); +} + +Trader.prototype.roundAmount = function(amount) { + return _.floor(amount, 8); +} + +Trader.prototype.roundPrice = function(price) { + return +price; +} + +Trader.prototype.isValidLot = function(price, amount) { + // Error: Total must be at least 0.0001. + return amount * price >= 0.0001; +} + +Trader.prototype.createOrder = function(side, amount, price, callback) { + const handle = (err, result) => { + if(err) { + console.log('createOrder', {side, amount, price}); + return callback(err); + } + + // console.log(new Date, 'PORDER created', result.orderNumber); + + callback(undefined, result.orderNumber); + } + + const fetch = next => { + this.poloniex[side](this.currency, this.asset, price, amount, this.processResponse(next, 'order', side)) + }; + retry(null, fetch, handle); +} + +Trader.prototype.buy = function(amount, price, callback) { + this.createOrder('buy', amount, price, callback); +} + +Trader.prototype.sell = function(amount, price, callback) { + this.createOrder('sell', amount, price, callback); +} + +Trader.prototype.checkOrder = function(id, callback) { + const handle = (err, result) => { + + if(err) { + return callback(err); + } + + if(result.completed) { + return callback(undefined, { executed: true, open: false }); + } + + const order = _.find(result, function(o) { return o.orderNumber === id }); + if(!order) { + console.log(new Date, 'order not open', id, result); + // if the order is not open it's fully executed + return callback(undefined, { executed: true, open: false }); + } + + callback(undefined, { executed: false, open: true, filledAmount: order.startingAmount - order.amount }); + } + + const fetch = next => this.poloniex.myOpenOrders(this.currency, this.asset, this.processResponse(next, 'checkOrder')); + retry(null, fetch, handle); +} + +Trader.prototype.getOrder = function(order, callback) { + const handle = (err, result) => { + if(err) + return callback(err); + + let price = 0; + let amount = 0; + let date = moment(0); + + if(result.unfilled) { + return callback(null, {price, amount, date}); + } + + _.each(result, trade => { + date = moment(trade.date); + price = ((price * amount) + (+trade.rate * trade.amount)) / (+trade.amount + amount); + amount += +trade.amount; + }); + + const fees = {}; + const feePercent = _.first(result).fee * 100; + + if(_.first(result).type === 'sell') { + const fee = price * amount * _.first(result).fee; + fees[this.currency] = fee; + } else { + const fee = amount * _.first(result).fee; + fees[this.asset] = fee; + } + + callback(err, {price, amount, date, fees, feePercent}); + }; + + const fetch = next => this.poloniex.returnOrderTrades(order, this.processResponse(next, 'getOrder', order)); + retry(null, fetch, handle); +} + +Trader.prototype.cancelOrder = function(order, callback) { + const handle = (err, result) => { + if(err) { + return callback(err); + } + + // console.log(new Date, 'PORDER cancel', order); + + if(result.filled) { + console.log(new Date, 'result.filled', result); + return callback(undefined, true); + } + + let data; + + if(result.amount) { + data = { remaining: result.amount }; + } else { + console.log(new Date, 'not result.amount', result); + } + + + callback(undefined, false, data); + }; + + const fetch = next => this.poloniex.cancelOrder(this.currency, this.asset, order, this.processResponse(next, 'cancelOrder', order)); + retry(null, fetch, handle); +} + +Trader.prototype.getTrades = function(since, callback, descending) { + + const firstFetch = !!since; + const args = _.toArray(arguments); + + const handle = (err, result) => { + if(err) + return callback(err); + + // Edge case, see here: + // @link https://github.com/askmike/gekko/issues/479 + if(firstFetch && _.size(result) === 50000) + return callback( + [ + 'Poloniex did not provide enough data. Read this:', + 'https://github.com/askmike/gekko/issues/479' + ].join('\n\n') + ); + + result = _.map(result, function(trade) { + return { + tid: trade.tradeID, + amount: +trade.amount, + date: moment.utc(trade.date).unix(), + price: +trade.rate + }; + }); + + callback(null, result.reverse()); + }; + + var params = { + currencyPair: this.pair + } + + if(since) + params.start = since.unix(); + + const fetch = next => this.poloniex._public('returnTradeHistory', params, this.processResponse(next, 'getTrades', since)); + retry(null, fetch, handle); +} + +Trader.getCapabilities = function () { + return { + name: 'Poloniex', + slug: 'poloniex', + currencies: marketData.currencies, + assets: marketData.assets, + markets: marketData.markets, + currencyMinimums: {BTC: 0.0001, ETH: 0.0001, XMR: 0.0001, USDT: 1.0}, + requires: ['key', 'secret'], + tid: 'tid', + providesHistory: 'date', + providesFullHistory: true, + tradable: true, + gekkoBroker: 0.6 + }; +} + +module.exports = Trader; diff --git a/exchange/wrappers/quadriga-markets.json b/exchange/wrappers/quadriga-markets.json new file mode 100644 index 0000000..d8c9163 --- /dev/null +++ b/exchange/wrappers/quadriga-markets.json @@ -0,0 +1,136 @@ +{ + "assets": [ + "XBT", + "ETH", + "LTC", + "BCH", + "BTG" + ], + "currencies": [ + "CAD", + "USD", + "XBT" + ], + "markets": [ + { + "pair": [ + "CAD", + "XBT" + ], + "book": "btc_cad", + "minimalOrder": { + "amount": "0.0005", + "unit": "asset" + }, + "precision": 8 + }, + { + "pair": [ + "USD", + "XBT" + ], + "book": "btc_usd", + "minimalOrder": { + "amount": "0.0005", + "unit": "asset" + }, + "precision": 8 + }, + { + "pair": [ + "CAD", + "BCH" + ], + "book": "bch_cad", + "minimalOrder": { + "amount": "0.0005", + "unit": "asset" + }, + "precision": 8 + }, + { + "pair": [ + "XBT", + "BCH" + ], + "book": "bch_btc", + "minimalOrder": { + "amount": "0.0005", + "unit": "asset" + }, + "precision": 8 + }, + { + "pair": [ + "CAD", + "ETH" + ], + "book": "eth_cad", + "minimalOrder": { + "amount": "0.0005", + "unit": "asset" + }, + "precision": 8 + }, + { + "pair": [ + "XBT", + "ETH" + ], + "book": "eth_btc", + "minimalOrder": { + "amount": "0.0005", + "unit": "asset" + }, + "precision": 8 + }, + { + "pair": [ + "CAD", + "LTC" + ], + "book": "ltc_cad", + "minimalOrder": { + "amount": "0.0005", + "unit": "asset" + }, + "precision": 8 + }, + { + "pair": [ + "XBT", + "LTC" + ], + "book": "ltc_btc", + "minimalOrder": { + "amount": "0.0005", + "unit": "asset" + }, + "precision": 8 + }, + { + "pair": [ + "CAD", + "BTG" + ], + "book": "btg_cad", + "minimalOrder": { + "amount": "0.0005", + "unit": "asset" + }, + "precision": 8 + }, + { + "pair": [ + "XBT", + "BTG" + ], + "book": "btg_btc", + "minimalOrder": { + "amount": "0.0005", + "unit": "asset" + }, + "precision": 8 + } + ] +} diff --git a/exchange/wrappers/quadriga.js.old b/exchange/wrappers/quadriga.js.old new file mode 100644 index 0000000..d462dc8 --- /dev/null +++ b/exchange/wrappers/quadriga.js.old @@ -0,0 +1,251 @@ +var QuadrigaCX = require('quadrigacx'); +var moment = require('moment'); +var util = require('../core/util'); +var _ = require('lodash'); +var log = require('../core/log'); + + +var Trader = function(config) { + _.bindAll(this); + + if(_.isObject(config)) { + this.key = config.key; + this.secret = config.secret; + this.clientId = config.username; + this.asset = config.asset; + this.currency = config.currency; + } + + this.pair = this.asset.toLowerCase() + '_' + this.currency.toLowerCase(); + this.name = 'quadriga'; + this.since = null; + + this.quadriga = new QuadrigaCX( + this.clientId ? this.clientId : "1", + this.key ? this.key : "", + this.secret ? this.secret : "", + ); +} + +Trader.prototype.retry = function(method, warn, args, error) { + var wait = +moment.duration(30, 'seconds'); + if (error.code === 200) { + log.debug(`${this.name}: API rate limit exceeded! unable to call ${method}, will retry in 2 minutes`) + wait = +moment.duration(120, 'seconds'); + } + else { + log.debug(JSON.stringify(error)); + log.debug(`${this.name}: ${warn}, will retry in 30 seconds`); + } + + var self = this; + + // make sure the callback (and any other fn) + // is bound to Trader + _.each(args, function(arg, i) { + if(_.isFunction(arg)) + args[i] = _.bind(arg, self); + }); + + // run the failed method again with the same + // arguments after wait + setTimeout( + function() { method.apply(self, args) }, + wait + ); +}; + +Trader.prototype.getTrades = function(since, callback, descending) { + var args = _.toArray(arguments); + var process = function(err, trades) { + if (trades && trades.error) return this.retry(this.getTrades, 'unable to get trades', args, trades.error); + if (err) return this.retry(this.getTrades, 'unable to get trades', args, err); + + var parsedTrades = []; + _.each(trades, function(trade) { + parsedTrades.push({ + tid: trade.tid, + date: trade.date, + price: parseFloat(trade.price), + amount: parseFloat(trade.amount) + }); + }, this); + + if(descending) + callback(null, parsedTrades); + else + callback(null, parsedTrades.reverse()); + }; + + var reqData = { + book: this.pair, + time: 'hour' + }; + + this.quadriga.api('transactions', reqData, _.bind(process, this)); +}; + +Trader.prototype.getPortfolio = function(callback) { + var args = _.toArray(arguments); + var set = function(err, data) { + + if (data && data.error) return this.retry(this.getPortfolio, 'unable to get balance', args, data.error); + if (err) return this.retry(this.getPortfolio, 'unable to get balance', args, err); + + var assetAmount = parseFloat( data[this.asset.toLowerCase() + '_available'] ); + var currencyAmount = parseFloat( data[this.currency.toLowerCase() + '_available'] ); + + if(!_.isNumber(assetAmount) || _.isNaN(assetAmount)) { + log.error(`Quadriga did not return balance for ${this.asset.toLowerCase()}, assuming 0.`); + assetAmount = 0; + } + + if(!_.isNumber(currencyAmount) || _.isNaN(currencyAmount)) { + log.error(`Quadriga did not return balance for ${this.currency.toLowerCase()}, assuming 0.`); + currencyAmount = 0; + } + + var portfolio = [ + { name: this.asset, amount: assetAmount }, + { name: this.currency, amount: currencyAmount } + ]; + callback(err, portfolio); + }; + + this.quadriga.api('balance', _.bind(set, this)); +}; + +Trader.prototype.getFee = function(callback) { + callback(false, 0.005); +}; + +Trader.prototype.getTicker = function(callback) { + var set = function(err, data) { + + if (data && data.error) return this.retry(this.getTicker, 'unable to get quote', args, data.error); + if (err) return this.retry(this.getTicker, 'unable to get quote', args, err); + + var ticker = { + ask: data.ask, + bid: data.bid + }; + callback(err, ticker); + }; + + this.quadriga.api('ticker', {book: this.pair}, _.bind(set, this)); +}; + +Trader.prototype.roundAmount = function(amount) { + var precision = 100000000; + var market = Trader.getCapabilities().markets.find(function(market){ return market.pair[0] === this.currency && market.pair[1] === this.asset }); + + if(Number.isInteger(market.precision)) + precision = 10 * market.precision; + + amount *= precision; + amount = Math.floor(amount); + amount /= precision; + return amount; +}; + +Trader.prototype.addOrder = function(tradeType, amount, price, callback) { + var args = _.toArray(arguments); + + amount = this.roundAmount(amount); + log.debug(tradeType.toUpperCase(), amount, this.asset, '@', price, this.currency); + + var set = function(err, data) { + + if (data && data.error) return this.retry(this.addOrder, 'unable to place order', args, data.error); + if (err) return this.retry(this.addOrder, 'unable to place order', args, err); + + var txid = data.id; + log.debug('added order with txid:', txid); + + callback(undefined, txid); + }; + + this.quadriga.api(tradeType, { + book: this.pair, + price: price, + amount: amount + }, _.bind(set, this)); +}; + + +Trader.prototype.getOrder = function(order, callback) { + var args = _.toArray(arguments); + + var get = function(err, data) { + if (data && data.error) return this.retry(this.getOrder, 'unable to get order', args, data.error); + if (err) return this.retry(this.getOrder, 'unable to get order', args, err); + + var price = parseFloat( data[0].price ); + var amount = parseFloat( data[0].amount ); + var date = (data[0].updated) ? moment.unix( data[0].updated ) : false; + + callback(undefined, {price, amount, date}); + }.bind(this); + + this.quadriga.api('lookup_oder', {id: order}, get); +} + +Trader.prototype.buy = function(amount, price, callback) { + this.addOrder('buy', amount, price, callback); +}; + +Trader.prototype.sell = function(amount, price, callback) { + this.addOrder('sell', amount, price, callback); +}; + +Trader.prototype.checkOrder = function(order, callback) { + var args = _.toArray(arguments); + + var check = function(err, data) { + + if (data && data.error) return this.retry(this.checkOrder, 'unable to get order', args, data.error); + if (err) return this.retry(this.checkOrder, 'unable to get order', args, err); + + var result = data[0]; + var stillThere = result.status === 0 || result.status === 1; + callback(err, !stillThere); + }; + + this.quadriga.api('lookup_order', {id: order}, _.bind(check, this)); +}; + +Trader.prototype.cancelOrder = function(order, callback) { + var args = _.toArray(arguments); + var cancel = function(err, data) { + + if (data && data.error) return this.retry(this.cancelOrder, 'unable to cancel order', args, data.error); + if (err) return this.retry(this.cancelOrder, 'unable to cancel order', args, err); + + callback(); + }; + + this.quadriga.api('cancel_order', {id: order}, _.bind(cancel, this)); +}; + +Trader.getCapabilities = function () { + return { + name: 'Quadriga', + slug: 'quadriga', + currencies: ['CAD', 'USD', 'BTC'], + assets: ['BTC', 'ETH', 'LTC', 'BCH'], + markets: [ + { pair: ['BTC', 'ETH'], minimalOrder: { amount: 0.00001, unit: 'asset' }, precision: 8 }, + { pair: ['CAD', 'ETH'], minimalOrder: { amount: 0.00001, unit: 'asset' }, precision: 8 }, + { pair: ['USD', 'BTC'], minimalOrder: { amount: 0.00001, unit: 'asset' }, precision: 8 }, + { pair: ['CAD', 'BTC'], minimalOrder: { amount: 0.00001, unit: 'asset' }, precision: 8 }, + { pair: ['CAD', 'LTC'], minimalOrder: { amount: 0.00001, unit: 'asset' }, precision: 8 }, + { pair: ['CAD', 'BCH'], minimalOrder: { amount: 0.00001, unit: 'asset' }, precision: 8 }, + ], + requires: ['key', 'secret', 'username'], + providesHistory: false, + tid: 'tid', + tradable: true + }; +} + +module.exports = Trader; diff --git a/exchange/wrappers/therocktrading-markets.json b/exchange/wrappers/therocktrading-markets.json new file mode 100644 index 0000000..bb73727 --- /dev/null +++ b/exchange/wrappers/therocktrading-markets.json @@ -0,0 +1,187 @@ +{ + "assets": [ + "ETH", + "LTC", + "BTC", + "BCH", + "EUR", + "PPC", + "ZEC", + "NOKU", + "FDZ" + ], + "currencies": [ + "BTC", + "EUR", + "ETH", + "GUSD", + "XRP" + ], + "markets": [ + { + "pair": [ + "EUR", + "BTC" + ], + "minimalOrder": { + "amount": 0.0005, + "price": 0.01, + "order": 0 + } + }, + { + "pair": [ + "EUR", + "ETH" + ], + "minimalOrder": { + "amount": 0.005, + "price": 0.01, + "order": 0 + } + }, + { + "pair": [ + "EUR", + "LTC" + ], + "minimalOrder": { + "amount": 0.01, + "price": 0.01, + "order": 0 + } + }, + { + "pair": [ + "EUR", + "PPC" + ], + "minimalOrder": { + "amount": 0.05, + "price": 0.01, + "order": 0 + } + }, + { + "pair": [ + "EUR", + "ZEC" + ], + "minimalOrder": { + "amount": 0.005, + "price": 0.01, + "order": 0 + } + }, + { + "pair": [ + "EUR", + "BCH" + ], + "minimalOrder": { + "amount": 0.005, + "price": 0.01, + "order": 0 + } + }, + { + "pair": [ + "EUR", + "GUSD" + ], + "minimalOrder": { + "amount": 0.01, + "price": 0.01, + "order": 0 + } + }, + { + "pair": [ + "BTC", + "ETH" + ], + "minimalOrder": { + "amount": 0.001, + "price": 1e-8, + "order": 0 + } + }, + { + "pair": [ + "BTC", + "PPC" + ], + "minimalOrder": { + "amount": 0.05, + "price": 1e-8, + "order": 0 + } + }, + { + "pair": [ + "BTC", + "LTC" + ], + "minimalOrder": { + "amount": 0.01, + "price": 1e-8, + "order": 0 + } + }, + { + "pair": [ + "BTC", + "BCH" + ], + "minimalOrder": { + "amount": 0.005, + "price": 1e-8, + "order": 0 + } + }, + { + "pair": [ + "BTC", + "ZEC" + ], + "minimalOrder": { + "amount": 0.005, + "price": 1e-8, + "order": 0 + } + }, + { + "pair": [ + "GUSD", + "BTC" + ], + "minimalOrder": { + "amount": 0.0005, + "price": 0.01, + "order": 0 + } + }, + { + "pair": [ + "XRP", + "EUR" + ], + "minimalOrder": { + "amount": 0.01, + "price": 0.01, + "order": 0 + } + }, + { + "pair": [ + "XRP", + "BTC" + ], + "minimalOrder": { + "amount": 0.005, + "price": 0.01, + "order": 0 + } + } + ] +} diff --git a/exchange/wrappers/therocktrading.js b/exchange/wrappers/therocktrading.js new file mode 100644 index 0000000..e936ff4 --- /dev/null +++ b/exchange/wrappers/therocktrading.js @@ -0,0 +1,286 @@ +const Therocktrading = require('therocktrading'); +const _ = require('lodash'); +const moment = require('moment'); +const retry = require('../exchangeUtils').retry; +const marketData = require("./therocktrading-markets.json"); + +const QUERY_DELAY = 350; + +const Trader = function(config) { + this.post_only = true; + this.use_sandbox = false; + this.name = 'Therocktrading'; + this.scanback = true; + this.scanbackTid = 0; + this.since = null; + this.asset = config.asset; + this.currency = config.currency; + + this.api_url = 'https://api.therocktrading.com'; + this.api_sandbox_url = 'https://api-staging.therocktrading.com'; + + if (_.isObject(config)) { + this.key = config.key; + this.secret = config.secret; + + this.pair = [config.asset, config.currency].join('').toUpperCase(); + this.post_only = + typeof config.post_only !== 'undefined' ? config.post_only : true; + + if (config.sandbox) { + this.use_sandbox = config.sandbox; + } + + } + + this.therocktrading = new Therocktrading(this.key, this.secret, 'Gekko Broker v' + require('../package.json').version); +}; + +const recoverableErrors = [ + 'SOCKETTIMEDOUT', + 'TIMEDOUT', + 'CONNRESET', + 'CONNREFUSED', + 'NOTFOUND', + 'Rate limit exceeded', + 'Response code 5', + 'Therocktrading is currently under maintenance.', + 'HTTP 408 Error', + 'HTTP 504 Error', + 'HTTP 503 Error', + 'EHOSTUNREACH', + 'EAI_AGAIN', + 'ENETUNREACH' +]; + +const includes = (str, list) => { + if(!_.isString(str)) + return false; + + return _.some(list, item => str.includes(item)); +} + +Trader.prototype.processResponse = function(method, next) { + return (error, body) => { + if (body && body.code) { + error = new Error(`Error ${body.code}: ${body.msg}`); + } + + if(!error && body && !_.isEmpty(body.message)) { + error = new Error(body.message); + } + + /*if( + response && + response.statusCode < 200 && + response.statusCode >= 300 + ) { + error = new Error(`Response code ${response.statusCode}`); + }*/ + + if(error) { + if(_.isString(error)) { + error = new Error(error); + } + + if(includes(error.message, recoverableErrors)) { + error.notFatal = true; + } + + return next(error); + } + + return next(undefined, body); + } +}; + +Trader.prototype.getTrades = function(since, callback, descending) { + const handle = (err, data) => { + if (err) return callback(err); + var trades = []; + if (_.isArray(data.trades)) { + trades = _.map(data.trades, function(trade) { + return { + tid: trade.id, + price: trade.price, + amount: trade.amount, + date: moment.utc(trade.date).format('X') + }; + }); + } + + callback(null, descending ? trades : trades.reverse()); + }; + + if (moment.isMoment(since)) since = since.format(); + //if (moment.isMoment(to)) to = to.format(); + + var options = { + order: "DESC" // which is default at therock + } + if (since) + options.after = since; + const fetch = cb => this.therocktrading.trades(this.pair, options, this.processResponse('getTrades', cb)); + retry(null, fetch, handle); +}; + + +Trader.prototype.getTicker = function(callback) { + const result = (err, data) => { + if (err) return callback(err); + callback(undefined, { bid: data.bid, ask: data.ask }); + }; + + const fetch = cb => + this.therocktrading.ticker(this.pair, this.processResponse('getTicker', cb)); + retry(null, fetch, result); +}; + +Trader.prototype.getFee = function(callback) { + const fee = 0.2; + // should check for discounts ? + callback(undefined, fee); +}; + +Trader.prototype.getPortfolio = function(callback) { + const result = (err, data) => { + if (err) return callback(err); + var portfolio = data.balances.map(function(balance) { + return { + name: balance.currency.toUpperCase(), + amount: parseFloat(balance.trading_balance), + }; + }); + callback(undefined, portfolio); + }; + + const fetch = cb => this.therocktrading.balances(this.processResponse('getPortfolio', cb)); + retry(undefined, fetch, result); +}; + + +Trader.prototype.buy = function(amount, price, callback) { + const handle = (err, result) => { + if(err) { + return callback(err); + } + callback(undefined, result.id); + } + const fetch = next => { + this.therocktrading.buy(this.pair, amount.toString(), price.toString(), this.processResponse('order', next)) + }; + retry(null, fetch, handle); +} + +Trader.prototype.sell = function(amount, price, callback) { + const handle = (err, result) => { + if(err) { + return callback(err); + } + callback(undefined, result.id); + } + const fetch = next => { + this.therocktrading.sell(this.pair, amount.toString(), price.toString(), this.processResponse('order', next)) + }; + retry(null, fetch, handle); +} + + +Trader.prototype.getOrder = function(order, callback) { + const handle = (err, result) => { + if(err) + return callback(err); + + let price = 0; + let amount = 0; + let date = moment(0); + + if(result.amount === result.amount_unfilled) { + return callback(null, {price, amount, date}); + } + + _.each(result.trades, trade => { + date = moment(trade.date); + price = ((price * amount) + (+trade.price * trade.amount)) / (+trade.amount + amount); + amount += +trade.amount; + }); + + const fees = {}; + const feePercent = this.makerFee; + + const fee = price * amount * feePercent; + fees[this.currency] = fee; + + callback(err, {price, amount, date, fees, feePercent}); + }; + + const fetch = cb => this.therocktrading.order_status(this.pair, order, this.processResponse('order_status', cb)); + retry(null, fetch, handle); +}; + +Trader.prototype.checkOrder = function(order, callback) { + const result = (err, data) => { + if (err) return callback(err); + + var status = data.status; + if (status == 'executed') { + return callback(undefined, { executed: true, open: false, filledAmount: parseFloat(data.amount) }); + } else if (status === 'deleted') { + return callback(undefined, { executed: false, open: false, filledAmount: parseFloat(data.amount - data.amount_unfilled) }); + } else if (status === 'active') { + // should check if not expired (data.close_on)? + return callback(undefined, { executed: false, open: true, filledAmount: parseFloat(data.amount - data.amount_unfilled) }); + } + + callback(new Error('Unknown status ' + status)); + }; + + const fetch = cb => + this.therocktrading.order_status(this.pair, order, this.processResponse('order_status', cb)); + retry(null, fetch, result); +}; + + + +Trader.prototype.cancelOrder = function(order, callback) { + // callback for cancelOrder should be true if the order was already filled, otherwise false + const result = (err, data) => { + if(err) { + return callback(null, true); // need to catch the specific error but usually an error on cancel means it was filled + } + + return callback(null, false); + }; + + const fetch = cb => + this.therocktrading.cancel_order(this.pair, order, this.processResponse('cancel_order', cb)); + retry(null, fetch, result); +}; + +Trader.prototype.roundAmount = function(amount) { + return _.floor(amount, 8); +} + +Trader.prototype.roundPrice = function(price) { + return +price; +} + +Trader.getCapabilities = function() { + return { + name: 'Therocktrading', + slug: 'therocktrading', + currencies: marketData.currencies, + assets: marketData.assets, + markets: marketData.markets, + requires: ['key', 'secret'], + providesHistory: 'date', + providesFullHistory: true, + // following tid, define type to use on trade to batch trades + tid: 'tid', + tradable: true, + gekkoBroker: 0.6 + }; +}; + +module.exports = Trader; + diff --git a/exchange/wrappers/wex.nz.js.old b/exchange/wrappers/wex.nz.js.old new file mode 100644 index 0000000..72d2719 --- /dev/null +++ b/exchange/wrappers/wex.nz.js.old @@ -0,0 +1,301 @@ +var WEX = require('node-wex'); + +var moment = require('moment'); +var util = require('../core/util'); +var _ = require('lodash'); +var log = require('../core/log') + +var Trader = function(config) { + this.key = config.key; + this.secret = config.secret; + this.asset = config.asset; + this.currency = config.currency; + this.pair = [config.asset, config.currency].join('_').toLowerCase(); + this.name = 'wex.nz'; + + _.bindAll(this); + + this.wex = new WEX(this.key, this.secret); + _.bindAll(this.wex, ['trade', 'trades', 'getInfo', 'ticker', 'orderList']); + + // see @link https://github.com/askmike/gekko/issues/302 + this.wexHistorocial = new WEX(false, false, {public_url: 'https://wex.nz/api/3/'}); + _.bindAll(this.wexHistorocial, 'trades'); +} + +Trader.prototype.round = function(amount) { + // Prevent "You incorrectly entered one of fields." + // because of more than 8 decimals. + amount *= 100000000; + amount = Math.floor(amount); + amount /= 100000000; + + return amount; +} + +Trader.prototype.buy = function(amount, price, callback) { + amount = this.round(amount); + + var set = function(err, data) { + if(err || !data || !data.return) + return log.error('unable to buy:', err); + + callback(null, data.return.order_id); + }.bind(this); + + // workaround for nonce error + setTimeout(function() { + this.wex.trade(this.pair, 'buy', price, amount, _.bind(set, this)); + }.bind(this), 1000); +} + +Trader.prototype.sell = function(amount, price, callback) { + amount = this.round(amount); + + var set = function(err, data) { + if(err || !data || !data.return) + return log.error('unable to sell:\n\n', err); + + callback(null, data.return.order_id); + }; + + // workaround for nonce error + setTimeout(function() { + this.wex.trade(this.pair, 'sell', price, amount, _.bind(set, this)); + }.bind(this), 1000); +} + +// if the exchange errors we try the same call again after +// waiting 10 seconds +Trader.prototype.retry = function(method, args) { + var wait = +moment.duration(10, 'seconds'); + log.debug(this.name, 'returned an error, retrying..'); + + var self = this; + + // run the failed method again with the same + // arguments after wait + setTimeout( + function() { method.apply(self, args) }, + wait + ); +} + +Trader.prototype.getPortfolio = function(callback) { + var args = _.toArray(arguments); + var calculate = function(err, data) { + + if(err) { + if(err.message === 'invalid api key') + util.die('Your ' + this.name + ' API keys are invalid'); + + return this.retry(this.getPortfolio, args); + } + + if(_.isEmpty(data)) + err = 'no data'; + + if (err || !data.return || !data.return.funds) + return this.retry(this.getPortfolio, args); + + var assetAmount = parseFloat( data.return.funds[this.asset.toLowerCase()] ); + var currencyAmount = parseFloat( data.return.funds[this.currency.toLowerCase()] ); + + if(!_.isNumber(assetAmount) || _.isNaN(assetAmount)) { + log.error(`wex.nz did not return portfolio for ${this.asset}, assuming 0.`); + assetAmount = 0; + } + + if(!_.isNumber(currencyAmount) || _.isNaN(currencyAmount)) { + log.error(`wex.nz did not return portfolio for ${this.currency}, assuming 0.`); + currencyAmount = 0; + } + + var portfolio = [ + { name: this.asset, amount: assetAmount }, + { name: this.currency, amount: currencyAmount } + ]; + + callback(err, portfolio); + }.bind(this); + + this.wex.getInfo(calculate); +} + +Trader.prototype.getTicker = function(callback) { + // wex.nz doesn't state asks and bids in its + // ticker + var set = function(err, data) { + + if(err) + return this.retry(this.wex.ticker, [this.pair, set]); + + var ticker = { + ask: data.ticker.buy, + bid: data.ticker.sell + }; + + callback(err, ticker); + }.bind(this); + + this.wex.ticker(this.pair, set); +} + +Trader.prototype.getFee = function(callback) { + // wex.nz doesn't have different fees based on orders + // at this moment it is always 0.2% + callback(false, 0.002); +} + +Trader.prototype.checkOrder = function(order, callback) { + var check = function(err, result) { + // wex returns an error when you have no open trades + // right now we assume on every error that the order + // was filled. + // + // TODO: check whether the error states that there are no + // open trades or that there is something else. + if(err) + callback(false, true); + else + callback(err, !result[order]); + }.bind(this); + + this.wex.orderList({}, check); +} + +Trader.prototype.getOrder = function(orderId, callback) { + console.log('getOrder', orderId); + var args = _.toArray(arguments); + var check = function(err, result) { + if(err) { + log.error('error on getOrder', err); + return this.retry(this.getOrder, args); + } + + var order = null; + + _.each(result.return, o => { + if(o.order_id === +orderId) + order = o; + }); + + if(!order) + return log.error('wex.nz did not provide the order'); + + var price = parseFloat(order.rate); + var amount = parseFloat(order.amount); + var date = moment.unix(order.timestamp); + + callback(undefined, {price, amount, date}); + }.bind(this); + + this.wex.tradeHistory({pair: this.pair}, check); +} + + +Trader.prototype.cancelOrder = function(order, callback) { + this.wex.cancelOrder(order, (err, result) => { + callback(); + }); +} + +Trader.prototype.getTrades = function(since, callback, descending) { + var args = _.toArray(arguments); + var process = function(err, trades) { + if(err) + return this.retry(this.getTrades, args); + + if(descending) + callback(false, trades); + else + callback(false, trades.reverse()); + }.bind(this); + + // see @link https://github.com/askmike/gekko/issues/302 + if(since) { + this.wexHistorocial.makePublicApiRequest( + 'trades', + this.pair + '?limit=2000', + this.processAPIv3Trades(process) + ) + } else + this.wex.trades(this.pair, process); +} + +Trader.prototype.processAPIv3Trades = function(cb) { + return function(err, data) { + var trades = _.map(data[this.pair], function(t) { + return { + price: t.price, + amount: t.amount, + tid: t.tid, + date: t.timestamp + } + }) + cb(err, trades); + }.bind(this) +} + +Trader.getCapabilities = function () { + return { + name: 'wex.nz', + slug: 'wex.nz', + currencies: ['USD', 'RUR', 'EUR', 'BTC', 'LTC', 'ETH', 'NMC', 'NMC', 'NVC', 'PPC', 'DSH', 'BCH'], + assets: [ + 'BTC', 'LTC', 'NMC', 'NVC', 'USD', 'EUR', 'PPC', 'DSH', 'ETH', + 'USDET', 'RURET', 'EURET', 'BTCET', 'LTCET', 'ETHET', 'NMCET', 'NVCET', 'PPCET', 'DSHET', 'BCHET' // Token + ], + markets: [ + { pair: ['USD', 'BTC'], minimalOrder: { amount: 0.01, unit: 'asset' } }, + { pair: ['RUR', 'BTC'], minimalOrder: { amount: 0.01, unit: 'asset' } }, + { pair: ['EUR', 'BTC'], minimalOrder: { amount: 0.01, unit: 'asset' } }, + { pair: ['BTC', 'LTC'], minimalOrder: { amount: 0.1, unit: 'asset' } }, + { pair: ['USD', 'LTC'], minimalOrder: { amount: 0.1, unit: 'asset' } }, + { pair: ['RUR', 'LTC'], minimalOrder: { amount: 0.1, unit: 'asset' } }, + { pair: ['EUR', 'LTC'], minimalOrder: { amount: 0.1, unit: 'asset' } }, + { pair: ['BTC', 'NMC'], minimalOrder: { amount: 0.1, unit: 'asset' } }, + { pair: ['USD', 'NMC'], minimalOrder: { amount: 0.1, unit: 'asset' } }, + { pair: ['BTC', 'NVC'], minimalOrder: { amount: 0.1, unit: 'asset' } }, + { pair: ['USD', 'NVC'], minimalOrder: { amount: 0.1, unit: 'asset' } }, + { pair: ['RUR', 'USD'], minimalOrder: { amount: 0.1, unit: 'asset' } }, + { pair: ['USD', 'EUR'], minimalOrder: { amount: 0.1, unit: 'asset' } }, + { pair: ['RUR', 'EUR'], minimalOrder: { amount: 0.1, unit: 'asset' } }, + { pair: ['BTC', 'PPC'], minimalOrder: { amount: 0.1, unit: 'asset' } }, + { pair: ['USD', 'PPC'], minimalOrder: { amount: 0.1, unit: 'asset' } }, + { pair: ['BTC', 'DSH'], minimalOrder: { amount: 0.1, unit: 'asset' } }, + { pair: ['USD', 'DSH'], minimalOrder: { amount: 0.1, unit: 'asset' } }, + { pair: ['RUR', 'DSH'], minimalOrder: { amount: 0.1, unit: 'asset' } }, + { pair: ['EUR', 'DSH'], minimalOrder: { amount: 0.1, unit: 'asset' } }, + { pair: ['LTC', 'DSH'], minimalOrder: { amount: 0.1, unit: 'asset' } }, + { pair: ['ETH', 'DSH'], minimalOrder: { amount: 0.1, unit: 'asset' } }, + { pair: ['BTC', 'ETH'], minimalOrder: { amount: 0.1, unit: 'asset' } }, + { pair: ['USD', 'ETH'], minimalOrder: { amount: 0.1, unit: 'asset' } }, + { pair: ['EUR', 'ETH'], minimalOrder: { amount: 0.1, unit: 'asset' } }, + { pair: ['LTC', 'ETH'], minimalOrder: { amount: 0.1, unit: 'asset' } }, + { pair: ['RUR', 'ETH'], minimalOrder: { amount: 0.1, unit: 'asset' } }, + { pair: ['USD', 'BCH'], minimalOrder: { amount: 0.1, unit: 'asset' } }, + { pair: ['BTC', 'BCH'], minimalOrder: { amount: 0.1, unit: 'asset' } }, + + // Token pairs + { pair: ['USD', 'USDET'], minimalOrder: { amount: 0.1, unit: 'asset' } }, + { pair: ['RUR', 'RURET'], minimalOrder: { amount: 0.1, unit: 'asset' } }, + { pair: ['EUR', 'EURET'], minimalOrder: { amount: 0.1, unit: 'asset' } }, + { pair: ['BTC', 'BTCET'], minimalOrder: { amount: 0.1, unit: 'asset' } }, + { pair: ['LTC', 'LTCET'], minimalOrder: { amount: 0.1, unit: 'asset' } }, + { pair: ['ETH', 'ETHET'], minimalOrder: { amount: 0.1, unit: 'asset' } }, + { pair: ['NMC', 'NMCET'], minimalOrder: { amount: 0.1, unit: 'asset' } }, + { pair: ['NVC', 'NVCET'], minimalOrder: { amount: 0.1, unit: 'asset' } }, + { pair: ['PPC', 'PPCET'], minimalOrder: { amount: 0.1, unit: 'asset' } }, + { pair: ['DSH', 'DSHET'], minimalOrder: { amount: 0.1, unit: 'asset' } }, + { pair: ['BCH', 'BCHET'], minimalOrder: { amount: 0.1, unit: 'asset' } }, + + ], + requires: ['key', 'secret'], + providesHistory: false, + tid: 'tid', + tradable: true + }; +} + +module.exports = Trader; diff --git a/exchange/wrappers/zaif.jp.js.old b/exchange/wrappers/zaif.jp.js.old new file mode 100644 index 0000000..441c0ca --- /dev/null +++ b/exchange/wrappers/zaif.jp.js.old @@ -0,0 +1,144 @@ +var Zaif = require("zaif.jp"); +var util = require('../core/util.js'); +var _ = require('lodash'); +var moment = require('moment'); +var log = require('../core/log'); + +var Trader = function(config) { + _.bindAll(this); + if(_.isObject(config)) { + this.key = config.key; + this.secret = config.secret; + this.clientID = config.username; + } + this.name = 'Zaif'; + this.balance; + this.price; + + this.zaif = Zaif.createPrivateApi(this.key, this.secret, 'user agent is node-zaif'); + this.api = Zaif.PublicApi; +} + +// if the exchange errors we try the same call again after +// waiting 10 seconds +Trader.prototype.retry = function(method, args) { + var wait = +moment.duration(10, 'seconds'); + log.debug(this.name, 'returned an error, retrying..'); + + var self = this; + + // make sure the callback (and any other fn) + // is bound to Trader + _.each(args, function(arg, i) { + if(_.isFunction(arg)) + args[i] = _.bind(arg, self); + }); + + // run the failed method again with the same + // arguments after wait + setTimeout( + function() { method.apply(self, args) }, + wait + ); +} + +Trader.prototype.getPortfolio = function(callback) { + var set = function(data) { + var portfolio = []; + _.each(data.funds, function(amount, asset) { +// if(asset.indexOf('available') !== -1) { + asset = asset.substr(0, 3).toUpperCase(); + portfolio.push({name: asset, amount: parseFloat(amount)}); +// } + }); + callback(err, portfolio); + } + this.zaif.getInfo().then(_.bind(set, this)); +} + +Trader.prototype.getTicker = function(callback) { + this.api.ticker('btc_jpy').then(callback); +} + +Trader.prototype.getFee = function(callback) { + var makerFee = 0.0; + callback(false, makerFee / 100); +} + +Trader.prototype.buy = function(amount, price, callback) { + var set = function(result) { + if(!result) + return log.error('unable to buy:', result); + + callback(null, result.order_id); + }; + + // TODO: fees are hardcoded here? +// amount *= 0.995; // remove fees + // prevent: Ensure that there are no more than 8 digits in total. + amount *= 100000000; + amount = Math.floor(amount); + amount /= 100000000; + this.zaif.trade('bid', price, amount).then(_.bind(set, this)); +} + +Trader.prototype.sell = function(amount, price, callback) { + var set = function(result) { + if(!result) + return log.error('unable to sell:', result); + + callback(null, result.order_id); + }; + + this.zaif.trade('ask', price, amount).then(_.bind(set, this)); +} + +Trader.prototype.checkOrder = function(order, callback) { + var check = function(result) { + var stillThere = (order in result); + callback(null, !stillThere); + }; + + this.zaif.activeorders().then(_.bind(check, this)); +} + +Trader.prototype.cancelOrder = function(order, callback) { + var cancel = function(result) { + if(!result) + log.error('unable to cancel order', order, '(', result, ')'); + }; + + this.zaif.cancelorder(order).then(_.bind(cancel, this)); +} + +Trader.prototype.getTrades = function(since, callback, descending) { + var args = _.toArray(arguments); + var process = function(result) { + if(!result) + return this.retry(this.getTrades, args); + + callback(null, result.reverse()); + }; + + this.api.trades('btc_jpy').then(_.bind(process, this)); +} + +Trader.getCapabilities = function () { + return { + name: 'Zaif.jp', + slug: 'zaif.jp', + currencies: ['JPY'], + assets: ['BTC'], + markets: [ + { + pair: ['JPY', 'BTC'], minimalOrder: { amount: 1, unit: 'currency' } + } + ], + requires: ['key', 'secret', 'username'], + providesHistory: false, + fetchTimespan: 60, + tid: 'tid' + }; +} + +module.exports = Trader; \ No newline at end of file diff --git a/gekko.js b/gekko.js new file mode 100644 index 0000000..b0574fa --- /dev/null +++ b/gekko.js @@ -0,0 +1,63 @@ +/* + + Gekko is a Bitcoin trading bot for popular Bitcoin exchanges written + in node, it features multiple trading methods using technical analysis. + + If you are interested in how Gekko works, read more about Gekko's + architecture here: + + https://gekko.wizb.it/docs/internals/architecture.html + + Disclaimer: + + USE AT YOUR OWN RISK! + + The author of this project is NOT responsible for any damage or loss caused + by this software. There can be bugs and the bot may not perform as expected + or specified. Please consider testing it first with paper trading and/or + backtesting on historical data. Also look at the code to see what how + it is working. + +*/ + +console.log(` + ______ ________ __ __ __ __ ______ + / \\ / |/ | / |/ | / | / \\ + /$$$$$$ |$$$$$$$$/ $$ | /$$/ $$ | /$$/ /$$$$$$ | + $$ | _$$/ $$ |__ $$ |/$$/ $$ |/$$/ $$ | $$ | + $$ |/ |$$ | $$ $$< $$ $$< $$ | $$ | + $$ |$$$$ |$$$$$/ $$$$$ \\ $$$$$ \\ $$ | $$ | + $$ \\__$$ |$$ |_____ $$ |$$ \\ $$ |$$ \\ $$ \\__$$ | + $$ $$/ $$ |$$ | $$ |$$ | $$ |$$ $$/ + $$$$$$/ $$$$$$$$/ $$/ $$/ $$/ $$/ $$$$$$/ +`); + +const util = require(__dirname + '/core/util'); + +console.log('\tGekko v' + util.getVersion()); +console.log('\tI\'m gonna make you rich, Bud Fox.', '\n\n'); + +const dirs = util.dirs(); + +if(util.launchUI()) { + return require(util.dirs().web + 'server'); +} + +const pipeline = require(dirs.core + 'pipeline'); +const config = util.getConfig(); +const mode = util.gekkoMode(); + +if( + config.trader && + config.trader.enabled && + !config['I understand that Gekko only automates MY OWN trading strategies'] +) + util.die('Do you understand what Gekko will do with your money? Read this first:\n\nhttps://github.com/askmike/gekko/issues/201'); + +// > Ever wonder why fund managers can't beat the S&P 500? +// > 'Cause they're sheep, and sheep get slaughtered. +pipeline({ + config: config, + mode: mode +}); + diff --git a/importers/exchanges/binance.js b/importers/exchanges/binance.js new file mode 100644 index 0000000..aefc856 --- /dev/null +++ b/importers/exchanges/binance.js @@ -0,0 +1,60 @@ +const moment = require('moment'); +const util = require('../../core/util.js'); +const _ = require('lodash'); +const log = require('../../core/log'); + +var config = util.getConfig(); +var dirs = util.dirs(); + +var Fetcher = require(dirs.exchanges + 'binance'); + +util.makeEventEmitter(Fetcher); + +var end = false; +var done = false; +var from = false; + +var fetcher = new Fetcher(config.watch); + +var fetch = () => { + fetcher.import = true; + fetcher.getTrades(from, handleFetch); +}; + +var handleFetch = (err, trades) => { + if (err) { + log.error(`There was an error importing from Binance ${err}`); + fetcher.emit('done'); + return fetcher.emit('trades', []); +} + + if (trades.length > 0) { + var last = moment.unix(_.last(trades).date).utc(); + // Conversion to milliseconds epoch time means we have to compensate for possible leap seconds + var next = from.clone().add(1, 'h').subtract(1, 's'); + } else { + // Conversion to milliseconds epoch time means we have to compensate for possible leap seconds + var next = from.clone().add(1, 'h').subtract(1, 's'); + log.debug('Import step returned no results, moving to the next 1h period'); + } + + if (from.add(1, 'h') >= end) { + fetcher.emit('done'); + + var endUnix = end.unix(); + trades = _.filter(trades, t => t.date <= endUnix); + } + + from = next.clone(); + fetcher.emit('trades', trades); +}; + +module.exports = function(daterange) { + from = daterange.from.clone().utc(); + end = daterange.to.clone().utc(); + + return { + bus: fetcher, + fetch: fetch, + }; +}; diff --git a/importers/exchanges/bitfinex.js b/importers/exchanges/bitfinex.js new file mode 100644 index 0000000..70f32a7 --- /dev/null +++ b/importers/exchanges/bitfinex.js @@ -0,0 +1,148 @@ +const Bitfinex = require('bitfinex-api-node'); +const util = require('../../core/util.js'); +const _ = require('lodash'); +const moment = require('moment'); +const log = require('../../core/log'); + +const config = util.getConfig(); + +const dirs = util.dirs(); + +const Fetcher = require(dirs.exchanges + 'bitfinex'); +const retry = require(dirs.exchanges + '../exchangeUtils').retry; + +Fetcher.prototype.getTrades = function(upto, callback, descending) { + const handle = (err, data) => { + if (err) return callback(err); + + var trades = []; + if (_.isArray(data)) { + trades = _.map(data, function(trade) { + return { + tid: trade.ID, + date: moment(trade.MTS).format('X'), + price: +trade.PRICE, + amount: +Math.abs(trade.AMOUNT), + }; + }); + } + + callback(null, descending ? trades : trades.reverse()); + }; + + let path = 'trades/t' + this.pair + '/hist'; + if (upto) { + let start = moment(upto).subtract(1, 'd').valueOf(); + let end = moment(upto).valueOf(); + path += `?limit=1000&start=${start}&end=${end}`; + } + + log.debug('Querying trades with: ' + path); + const fetch = cb => this.bitfinex.makePublicRequest(path, this.handleResponse('getTrades', cb)); + retry(null, fetch, handle); +}; + +util.makeEventEmitter(Fetcher); + +var end = false; +var done = false; +var from = false; + +var lastTimestamp = false; +var lastId = false; + +var batch = []; +var batch_start = false; +var batch_end = false; +var batch_last = false; + +const SCANNING_STRIDE = 24; +const ITERATING_STRIDE = 2; +var stride = ITERATING_STRIDE; + +var fetcher = new Fetcher(config.watch); +fetcher.bitfinex = new Bitfinex(null, null, { version: 2, transform: true }).rest; + +var retryCritical = { + retries: 10, + factor: 1.2, + minTimeout: 70 * 1000, + maxTimeout: 120 * 1000, +}; + +var fetch = () => { + fetcher.import = true; + + if (lastTimestamp) { + // We need to slow this down to prevent hitting the rate limits + setTimeout(() => { + + // make sure we fetch with overlap from last batch + const since = lastTimestamp - 1000; + fetcher.getTrades(since, handleFetch); + }, 2500); + } else { + lastTimestamp = from.valueOf(); + batch_start = moment(from); + batch_end = moment(from).add(stride, 'h'); + + fetcher.getTrades(batch_end, handleFetch); + } +}; + +var handleFetch = (err, trades) => { + if (err) { + log.error(`There was an error importing from Bitfinex ${err}`); + fetcher.emit('done'); + return fetcher.emit('trades', []); + } + + trades = _.filter(trades, t => !lastId || t.tid < lastId); + + if (trades.length) { + stride = ITERATING_STRIDE; + batch = trades.concat(batch); + var last = moment.unix(_.first(trades).date); + lastTimestamp = last.valueOf(); + lastId = _.first(trades).tid; + } else { + stride = SCANNING_STRIDE; + lastTimestamp = moment(lastTimestamp) + .subtract(stride, 'h') + .valueOf(); + } + + // if we're not done the batch we need to refetch + if (trades.length && moment(lastTimestamp) >= batch_start) { + return fetch(); + } + + var lastBatch = batch; + + // in this case we've finished the last batch and are complete + if (batch_end.isSame(end)) { + fetcher.emit('done'); + } else { + // the batch if complete, lets advance to the next set + lastId = false; + batch = []; + batch_start = moment(batch_end); + batch_end = moment(batch_end).add(stride, 'h'); + + if (batch_end > end) batch_end = moment(end); + + lastTimestamp = batch_end.valueOf(); + } + + fetcher.emit('trades', lastBatch); +}; + +module.exports = function(daterange) { + from = daterange.from.clone(); + end = daterange.to.clone(); + + return { + bus: fetcher, + fetch: fetch, + }; +}; diff --git a/importers/exchanges/btcc.js b/importers/exchanges/btcc.js new file mode 100644 index 0000000..730fe8b --- /dev/null +++ b/importers/exchanges/btcc.js @@ -0,0 +1,89 @@ +var BTCChina = require('btc-china-fork'); +var util = require('../../core/util.js'); +var _ = require('lodash'); +var moment = require('moment'); +var log = require('../../core/log'); + +var config = util.getConfig(); + +var dirs = util.dirs(); + +var Fetcher = require(dirs.exchanges + 'btcc'); + +// patch getTrades.. +Fetcher.prototype.getTrades = function(fromTid, sinceTime, callback) { + var args = _.toArray(arguments); + var process = function(err, result) { + if(err) + return this.retry(this.getTrades, args); + + callback(result); + }.bind(this); + + if(sinceTime) + var params = { + limit: 1, + sincetype: 'time', + since: sinceTime + } + + else if(fromTid) + var params = { + limit: 5000, + since: fromTid + } + + this.btcc.getHistoryData(process, params); +} + +util.makeEventEmitter(Fetcher); + +var iterator = false; +var end = false; +var done = false; +var from = false; + +var fetcher = new Fetcher(config.watch); + +var fetch = () => { + if(!iterator) + fetcher.getTrades(false, from, handleFirstFetch); + else + fetcher.getTrades(iterator, false, handleFetch); +} + +// we use the first fetch to figure out +// the tid of the moment we want data from +var handleFirstFetch = trades => { + iterator = _.first(trades).tid; + fetch(); +} + +var handleFetch = trades => { + + iterator = _.last(trades).tid; + var last = moment.unix(_.last(trades).date); + + if(last > end) { + fetcher.emit('done'); + + var endUnix = end.unix(); + trades = _.filter( + trades, + t => t.date <= endUnix + ); + } + + fetcher.emit('trades', trades); +} + +module.exports = function (daterange) { + from = daterange.from.unix(); + end = daterange.to.clone(); + + return { + bus: fetcher, + fetch: fetch + } +} + diff --git a/importers/exchanges/coinfalcon.js b/importers/exchanges/coinfalcon.js new file mode 100644 index 0000000..2a84b93 --- /dev/null +++ b/importers/exchanges/coinfalcon.js @@ -0,0 +1,53 @@ +const moment = require('moment'); +const util = require('../../core/util.js'); +const _ = require('lodash'); +const log = require('../../core/log'); + +var config = util.getConfig(); +var dirs = util.dirs(); + +var Fetcher = require(dirs.exchanges + 'coinfalcon'); + +util.makeEventEmitter(Fetcher); + +var end = false; +var done = false; +var from = false; + +var fetcher = new Fetcher(config.watch); + +var fetch = () => { + fetcher.import = true; + log.debug('[CoinFalcon] Getting trades from: ', from); + fetcher.getTrades(from, handleFetch, true); +}; + +var handleFetch = (unk, trades) => { + if (trades.length > 0) { + var last = moment.unix(_.last(trades).date).utc(); + var next = last.clone(); + } else { + var next = from.clone().add(1, 'h'); + log.debug('Import step returned no results, moving to the next 1h period'); + } + + if (from.add(1, 'h') >= end) { + fetcher.emit('done'); + + var endUnix = end.unix(); + trades = _.filter(trades, t => t.date <= endUnix); + } + + from = next.clone(); + fetcher.emit('trades', trades); +}; + +module.exports = function(daterange) { + from = daterange.from.clone().utc(); + end = daterange.to.clone().utc(); + + return { + bus: fetcher, + fetch: fetch, + }; +}; diff --git a/importers/exchanges/gdax.js b/importers/exchanges/gdax.js new file mode 100644 index 0000000..c3a308b --- /dev/null +++ b/importers/exchanges/gdax.js @@ -0,0 +1,168 @@ +const util = require('../../core/util.js'); +const _ = require('lodash'); +const moment = require('moment'); +const log = require('../../core/log'); + +const config = util.getConfig(); + +const dirs = util.dirs(); + +const QUERY_DELAY = 350; +const BATCH_SIZE = 100; +const SCAN_ITER_SIZE = 50000; +const BATCH_ITER_SIZE = BATCH_SIZE * 10; + +const Fetcher = require(dirs.exchanges + 'gdax'); +const retry = require(dirs.exchanges + '../exchangeUtils').retry; + +Fetcher.prototype.getTrades = function(sinceTid, callback) { + let lastScan = 0; + + const handle = (err, data) => { + if (err) return callback(err); + + let result = _.map(data, function(trade) { + return { + tid: trade.trade_id, + amount: parseFloat(trade.size), + date: moment.utc(trade.time).format("X"), + price: parseFloat(trade.price) + }; + }); + + callback(null, result.reverse()); + }; + + const fetch = cb => this.gdax_public.getProductTrades(this.pair, { after: sinceTid, limit: BATCH_SIZE }, this.processResponse('getTrades', cb)); + retry(null, fetch, handle); +}; + +Fetcher.prototype.findFirstTrade = function(sinceTs, callback) { + let currentId = 0; + let sinceM = moment(sinceTs).utc(); + + log.info(`Scanning for the first trade ID to start batching requests, may take a few minutes ...`); + + const handle = (err, data) => { + if (err) return callback(err); + + let m = moment.utc(_.first(data).time); + let ts = m.valueOf(); + if (ts < sinceTs) { + log.info(`First trade ID for batching found ${currentId - SCAN_ITER_SIZE}`); + return callback(undefined, currentId - SCAN_ITER_SIZE); + } + + currentId = _.first(data).trade_id; + log.debug(`Have trade id ${currentId} for date ${_.first(data).time} ${sinceM.from(m, true)} to scan`); + + let nextScanId = currentId - SCAN_ITER_SIZE; + if (nextScanId <= SCAN_ITER_SIZE) { + currentId = BATCH_ITER_SIZE; + log.info(`First trade ID for batching found ${currentId}`); + return callback(undefined, currentId); + } + + setTimeout(() => { + const fetch = cb => this.gdax_public.getProductTrades(this.pair, { after: nextScanId, limit: 1 }, this.processResponse('getTrades', cb)); + retry(null, fetch, handle); + }, QUERY_DELAY); + } + + const fetch = cb => this.gdax_public.getProductTrades(this.pair, { limit: 1 }, this.processResponse('getTrades', cb)); + retry(null, fetch, handle); +} + +util.makeEventEmitter(Fetcher); + +let end = false; +let done = false; +let from = false; + +let batch = []; +let batchId = false; // Lowest ID for the current a batch + +let lastId = false; + +let latestId = false; +let latestMoment = false; + +let fetcher = new Fetcher(config.watch); + +let retryForever = { + forever: true, + factor: 1.2, + minTimeout: 10 * 1000, + maxTimeout: 120 * 1000 +}; + +let fetch = () => { + fetcher.import = true; + + // We are in the sub-iteration step for a given batch + if (lastId) { + setTimeout(() => { + fetcher.getTrades(lastId, handleFetch); + }, QUERY_DELAY); + } + // We are running the first query, and need to find the starting batch + else { + let process = (err, firstBatchId) => { + if (err) return handleFetch(err); + + batchId = firstBatchId; + fetcher.getTrades(batchId + 1, handleFetch); + } + fetcher.findFirstTrade(from.valueOf(), process); + } +} + +let handleFetch = (err, trades) => { + if (err) { + log.error(`There was an error importing from GDAX ${err}`); + fetcher.emit('done'); + return fetcher.emit('trades', []); + } + + if (trades.length) { + batch = trades.concat(batch); + + let last = moment.unix(_.first(trades).date).utc(); + lastId = _.first(trades).tid + + let latestTrade = _.last(trades); + if (!latestId || latestTrade.tid > latestId) { + latestId = latestTrade.tid; + latestMoment = moment.unix(latestTrade.date).utc(); + } + + // still doing sub-iteration in the batch + if (lastId >= (batchId - BATCH_ITER_SIZE) && last >= from) + return fetch(); + } + + batchId += BATCH_ITER_SIZE; + lastId = batchId + 1; + + if (latestMoment >= end) { + fetcher.emit('done'); + } + + let endUnix = end.unix(); + let startUnix = from.unix(); + batch = _.filter(batch, t => t.date >= startUnix && t.date <= endUnix); + + fetcher.emit('trades', batch); + batch = []; +} + +module.exports = function (daterange) { + + from = daterange.from.utc().clone(); + end = daterange.to.utc().clone(); + + return { + bus: fetcher, + fetch: fetch + } +} diff --git a/importers/exchanges/kraken.js b/importers/exchanges/kraken.js new file mode 100644 index 0000000..64b3d8e --- /dev/null +++ b/importers/exchanges/kraken.js @@ -0,0 +1,81 @@ +var _ = require('lodash'); +var moment = require('moment'); + +var util = require('../../core/util.js'); +var log = require('../../core/log'); + +var config = util.getConfig(); + +var dirs = util.dirs(); + +var Fetcher = require(dirs.exchanges + 'kraken'); + +util.makeEventEmitter(Fetcher); + +var end = false; +var done = false; +var from = false; + +var lastId = false; +var prevLastId = false; + +var fetcher = new Fetcher(config.watch); + +var fetch = () => { + fetcher.import = true; + + if (lastId) { + var tidAsTimestamp = lastId / 1000000; + setTimeout(() => { + fetcher.getTrades(tidAsTimestamp, handleFetch) + }, 500); + } + else + fetcher.getTrades(from, handleFetch); +} + +var handleFetch = (err, trades) => { + if(!err && !trades.length) { + console.log('no trades'); + err = 'No trades'; + } + + if (err) { + log.error(`There was an error importing from Kraken ${err}`); + fetcher.emit('done'); + return fetcher.emit('trades', []); + } + + var last = moment.unix(_.last(trades).date).utc(); + lastId = _.last(trades).tid + if(last < from) { + log.debug('Skipping data, they are before from date', last.format()); + return fetch(); + } + + if (last > end || lastId === prevLastId) { + fetcher.emit('done'); + + var endUnix = end.unix(); + trades = _.filter( + trades, + t => t.date <= endUnix + ) + } + + prevLastId = lastId + fetcher.emit('trades', trades); +} + +module.exports = function (daterange) { + + from = daterange.from.clone(); + end = daterange.to.clone(); + + return { + bus: fetcher, + fetch: fetch + } +} + + diff --git a/importers/exchanges/luno.js b/importers/exchanges/luno.js new file mode 100644 index 0000000..437b3a4 --- /dev/null +++ b/importers/exchanges/luno.js @@ -0,0 +1,111 @@ +const moment = require('moment'); +const util = require('../../core/util.js'); +const _ = require('lodash'); +const retry = require('../../exchange/exchangeUtils').retry; + +const config = util.getConfig(); +const dirs = util.dirs(); +const Fetcher = require(dirs.exchanges + 'luno'); + +util.makeEventEmitter(Fetcher); + +var end = false; +var from = false; +const REQUEST_INTERVAL = 5 * 1000; + +Fetcher.prototype.getTrades = function(since, callback, descending) { + const recoverableErrors = [ + 'SOCKETTIMEDOUT', + 'TIMEDOUT', + 'CONNRESET', + 'CONNREFUSED', + 'NOTFOUND' + ]; + + const processResponse = function(funcName, callback) { + return (error, body) => { + if (!error && !body) { + error = new Error('Empty response'); + } + + if (error) { + console.log(funcName, 'processResponse received ERROR:', error.message); + if (includes(error.message, recoverableErrors)) { + error.notFatal = true; + } + + if (includes(error.message, ['error 429'])) { + error.notFatal = true; + error.backoffDelay = 10000; + } + + return callback(error, undefined); + } + + return callback(undefined, body); + } + }; + + const process = (err, result) => { + if (err) { + console.log('Error importing trades:', err); + return; + } + trades = _.map(result.trades, function(t) { + return { + price: t.price, + date: Math.round(t.timestamp / 1000), + amount: t.volume, + tid: t.timestamp + }; + }); + callback(null, trades.reverse()); + } + + if (moment.isMoment(since)) since = since.valueOf(); + (_.isNumber(since) && since > 0) ? since: since = 0; + + console.log('importer getting trades from Luno since', moment.utc(since).format('YYYY-MM-DD HH:mm:ss'), 'UTC'); + + const handler = cb => this.luno.getTrades({ since: since, pair: this.pair }, processResponse('getTrades', cb)); + retry(null, handler, process); +} + +const fetcher = new Fetcher(config.watch); + +const fetch = () => { + fetcher.import = true; + setTimeout(() => fetcher.getTrades(from, handleFetch), REQUEST_INTERVAL); +}; + +const handleFetch = (err, trades) => { + if (err) { + console.log(`There was an error importing from Luno: ${err}`); + fetcher.emit('done'); + return fetcher.emit('trades', []); + } + + if (trades.length > 0) { + from = moment.utc(_.last(trades).tid + 1).clone(); + } else { + fetcher.emit('done'); + } + + if (from >= end) { + fetcher.emit('done'); + const endUnix = end.unix(); + trades = _.filter(trades, t => t.date <= endUnix); + } + + fetcher.emit('trades', trades); +}; + +module.exports = function(daterange) { + from = daterange.from.clone().utc(); + end = daterange.to.clone().utc(); + + return { + bus: fetcher, + fetch: fetch, + }; +}; diff --git a/importers/exchanges/poloniex.js b/importers/exchanges/poloniex.js new file mode 100644 index 0000000..85b6838 --- /dev/null +++ b/importers/exchanges/poloniex.js @@ -0,0 +1,126 @@ +const util = require('../../core/util.js'); +const _ = require('lodash'); +const moment = require('moment'); +const log = require('../../core/log'); +const retry = require('../../exchange/exchangeUtils').retry; + +const config = util.getConfig(); + +const dirs = util.dirs(); + +const Fetcher = require(dirs.exchanges + 'poloniex'); + +const batchSize = 60 * 2; // 2 hour +const overlapSize = 10; // 10 minutes + +// Helper methods +function joinCurrencies(currencyA, currencyB){ + return currencyA + '_' + currencyB; +} + +// patch getTrades.. +Fetcher.prototype.getTrades = function(range, callback) { + const handle = (err, result) => { + if(err) { + return callback(err); + } + + if(_.size(result) === 50000) { + // to many trades.. + util.die('too many trades..'); + } + + result = _.map(result, function(trade) { + return { + tid: trade.tradeID, + amount: +trade.amount, + date: moment.utc(trade.date).format('X'), + price: +trade.rate + }; + }); + + callback(result.reverse()); + }; + + const params = { + currencyPair: joinCurrencies(this.currency, this.asset) + } + + params.start = range.from.unix(); + params.end = range.to.unix(); + + const fetch = next => this.poloniex._public('returnTradeHistory', params, this.processResponse(next)); + retry(null, fetch, handle); +} + +util.makeEventEmitter(Fetcher); + +var iterator = false; +var end = false; +var done = false; + +var fetcher = new Fetcher(config.watch); + +var fetch = () => { + log.info( + config.watch.currency, + config.watch.asset, + 'Requesting data from', + iterator.from.format('YYYY-MM-DD HH:mm:ss') + ',', + 'to', + iterator.to.format('YYYY-MM-DD HH:mm:ss') + ); + + if(util.gekkoEnv === 'child-process') { + let msg = ['Requesting data from', + iterator.from.format('YYYY-MM-DD HH:mm:ss') + ',', + 'to', + iterator.to.format('YYYY-MM-DD HH:mm:ss')].join(''); + process.send({type: 'log', log: msg}); + } + fetcher.getTrades(iterator, handleFetch); +} + +var handleFetch = trades => { + iterator.from.add(batchSize, 'minutes').subtract(overlapSize, 'minutes'); + iterator.to.add(batchSize, 'minutes').subtract(overlapSize, 'minutes'); + + if(!_.size(trades)) { + // fix https://github.com/askmike/gekko/issues/952 + if(iterator.to.clone().add(batchSize * 4, 'minutes') > end) { + fetcher.emit('done'); + } + + return fetcher.emit('trades', []); + } + + var last = moment.unix(_.last(trades).date); + + if(last > end) { + fetcher.emit('done'); + + var endUnix = end.unix(); + trades = _.filter( + trades, + t => t.date <= endUnix + ); + } + + fetcher.emit('trades', trades); +} + +module.exports = function (daterange) { + iterator = { + from: daterange.from.clone(), + to: daterange.from.clone().add(batchSize, 'minutes') + } + end = daterange.to.clone(); + + return { + bus: fetcher, + fetch: fetch + } +} + + + diff --git a/importers/exchanges/therocktrading.js b/importers/exchanges/therocktrading.js new file mode 100644 index 0000000..e0baccc --- /dev/null +++ b/importers/exchanges/therocktrading.js @@ -0,0 +1,91 @@ +const moment = require('moment'); +const util = require('../../core/util'); +const log = require('../../core/log'); +const _ = require('lodash'); +const retry = require('../../exchange/exchangeUtils').retry; + +const config = util.getConfig(); +const dirs = util.dirs(); +const Fetcher = require(dirs.exchanges + 'therocktrading'); + +Fetcher.prototype.getTrades = function(since, to, page, callback, descending) { + const handle = (err, data) => { + if (err) return callback(err); + var trades = []; + if (_.isArray(data.trades)) { + trades = _.map(data.trades, function(trade) { + return { + tid: trade.id, + price: trade.price, + amount: trade.amount, + date: moment.utc(trade.date).format('X') + }; + }); + } + + callback(null, descending ? trades : trades.reverse()); + }; + + if (moment.isMoment(since)) since = since.format(); + if (moment.isMoment(to)) to = to.format(); + + let options = { + before: to, + after: since, + page: page, + order: "ASC", + per_page: 200 + } + const fetch = cb => this.therocktrading.trades(this.pair, options, this.processResponse('getTrades', cb)); + retry(null, fetch, handle); +}; + +util.makeEventEmitter(Fetcher); + +var end = false; +var done = false; +var from = false; + +var page = 1; + +var fetcher = new Fetcher(config.watch); + +var fetch = () => { + fetcher.import = true; + + log.debug("IMPORTER: starting fetcher") + + fetcher.getTrades(from, end, page, handleFetch); +} + +var handleFetch = (err, trades) => { + if(!err && !trades.length) { + console.log('no more trades'); + fetcher.emit('done'); + } + + if (err) { + log.error(`There was an error importing from TheRockTrading ${err}`); + fetcher.emit('done'); + return fetcher.emit('trades', []); + } + + if (trades.length > 0) { + page = page + 1; + } + + fetcher.emit('trades', trades); +} + +module.exports = function (daterange) { + + from = daterange.from.clone(); + end = daterange.to.clone(); + + return { + bus: fetcher, + fetch: fetch + } +} + + diff --git a/logs/.gitignore b/logs/.gitignore new file mode 100644 index 0000000..c96a04f --- /dev/null +++ b/logs/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore \ No newline at end of file diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..c84d0ac --- /dev/null +++ b/package-lock.json @@ -0,0 +1,1826 @@ +{ + "name": "gekko", + "version": "0.6.8", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@sinonjs/formatio": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@sinonjs/formatio/-/formatio-2.0.0.tgz", + "integrity": "sha512-ls6CAMA6/5gG+O/IdsBcblvnd8qcO/l1TYoNeAzp3wcISOxlPXQEus0mLcdwazEkWjaBdaJ3TaxmNgCLWwvWzg==", + "dev": true, + "requires": { + "samsam": "1.3.0" + } + }, + "abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" + }, + "accepts": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.5.tgz", + "integrity": "sha1-63d99gEXI6OxTopywIBcjoZ0a9I=", + "requires": { + "mime-types": "~2.1.18", + "negotiator": "0.6.1" + } + }, + "ajv": { + "version": "5.5.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz", + "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=", + "requires": { + "co": "^4.6.0", + "fast-deep-equal": "^1.0.0", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.3.0" + } + }, + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" + }, + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=" + }, + "any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha1-q8av7tzqUugJzcA3au0845Y10X8=" + }, + "aproba": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", + "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==" + }, + "are-we-there-yet": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz", + "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", + "requires": { + "delegates": "^1.0.0", + "readable-stream": "^2.0.6" + } + }, + "asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=" + }, + "asn1": { + "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.0" + } + }, + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" + }, + "assertion-error": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", + "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", + "dev": true + }, + "async": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/async/-/async-2.1.2.tgz", + "integrity": "sha1-YSpKtF70KnDN6Aa62G7m2wR+g4U=", + "requires": { + "lodash": "^4.14.0" + }, + "dependencies": { + "lodash": { + "version": "4.17.10", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.10.tgz", + "integrity": "sha512-UejweD1pDoXu+AD825lWwp4ZGtSwgnpZxb3JDViD7StjQz+Nb/6l093lx4OQ0foGWNRoc19mWy7BzL+UAK2iVg==" + } + } + }, + "async-limiter": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.0.tgz", + "integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg==" + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" + }, + "aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "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==" + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" + }, + "bcrypt-pbkdf": { + "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.3" + } + }, + "bitfinex-api-node": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/bitfinex-api-node/-/bitfinex-api-node-1.2.1.tgz", + "integrity": "sha512-pG4BMCD7T/R1vkLhLdHPim4Lbfbkdyt/yTaJ+A48vrzGsQO7MwxIRRs6rEx1Acm/vpsUyksbOaQyladh2T8Whw==", + "requires": { + "debug": "^2.2.0", + "lodash": "^4.17.4", + "request": "^2.67.0", + "request-promise": "^4.2.0", + "ws": "^3.0.0" + }, + "dependencies": { + "lodash": { + "version": "4.17.10", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.10.tgz", + "integrity": "sha512-UejweD1pDoXu+AD825lWwp4ZGtSwgnpZxb3JDViD7StjQz+Nb/6l093lx4OQ0foGWNRoc19mWy7BzL+UAK2iVg==" + }, + "ws": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-3.3.3.tgz", + "integrity": "sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA==", + "requires": { + "async-limiter": "~1.0.0", + "safe-buffer": "~5.1.0", + "ultron": "~1.1.0" + } + } + } + }, + "bluebird": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.1.tgz", + "integrity": "sha512-MKiLiV+I1AA596t9w1sQJ8jkiSr5+ZKi0WKrYGUn6d1Fx+Ij4tIj+m2WMQSGczs5jZVxV339chE8iwk6F64wjA==" + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "browser-stdout": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", + "dev": true + }, + "bytes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=" + }, + "caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" + }, + "chai": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.1.2.tgz", + "integrity": "sha1-D2RYS6ZC8PKs4oBiefTwbKI61zw=", + "dev": true, + "requires": { + "assertion-error": "^1.0.1", + "check-error": "^1.0.1", + "deep-eql": "^3.0.0", + "get-func-name": "^2.0.0", + "pathval": "^1.0.0", + "type-detect": "^4.0.0" + } + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + } + }, + "check-error": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", + "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=", + "dev": true + }, + "chownr": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.1.tgz", + "integrity": "sha512-j38EvO5+LHX84jlo6h4UzmOwi0UgW61WRyPtJz4qaadK5eY3BTS5TY/S1Stc3Uk2lIM6TPevAlULiEJwie860g==" + }, + "co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=" + }, + "co-body": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/co-body/-/co-body-5.2.0.tgz", + "integrity": "sha512-sX/LQ7LqUhgyaxzbe7IqwPeTr2yfpfUIQ/dgpKo6ZI4y4lpQA0YxAomWIY+7I7rHWcG02PG+OuPREzMW/5tszQ==", + "requires": { + "inflation": "^2.0.0", + "qs": "^6.4.0", + "raw-body": "^2.2.0", + "type-is": "^1.6.14" + } + }, + "co-from-stream": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/co-from-stream/-/co-from-stream-0.0.0.tgz", + "integrity": "sha1-GlzYztdyY5RglPo58kmaYyl7yvk=", + "requires": { + "co-read": "0.0.1" + } + }, + "co-fs": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/co-fs/-/co-fs-1.2.0.tgz", + "integrity": "sha1-pt8EXOWMBO7UVYb/Q4UDKBOrpk4=", + "requires": { + "co-from-stream": "0.0.0", + "thunkify": "0.0.1" + } + }, + "co-read": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/co-read/-/co-read-0.0.1.tgz", + "integrity": "sha1-+Bs+uKhmdf7FHj2IOn9WToc8k4k=" + }, + "code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" + }, + "colors": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/colors/-/colors-0.6.2.tgz", + "integrity": "sha1-JCP+ZnisDF2uiFLl0OW+CMmXq8w=" + }, + "combined-stream": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.6.tgz", + "integrity": "sha1-cj599ugBrFYTETp+RFqbactjKBg=", + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "commander": { + "version": "2.17.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.17.0.tgz", + "integrity": "sha512-477o1hdVORiFlZxw8wgsXYCef3lh0zl/OV0FTftqiDxJSWw6dPQ2ipS4k20J2qBcsmsmLKSyr2iFrf9e3JGi4w==" + }, + "composition": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/composition/-/composition-2.3.0.tgz", + "integrity": "sha1-dCgFN0yrVQxSCjNmL1pzLgII1vI=", + "requires": { + "any-promise": "^1.1.0", + "co": "^4.0.2" + } + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + }, + "console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=" + }, + "content-disposition": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", + "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ=" + }, + "content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" + }, + "cookies": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/cookies/-/cookies-0.7.1.tgz", + "integrity": "sha1-fIphX1SBxhq58WyDNzG8uPZjuZs=", + "requires": { + "depd": "~1.1.1", + "keygrip": "~1.0.2" + } + }, + "copy-to": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/copy-to/-/copy-to-2.0.1.tgz", + "integrity": "sha1-JoD7uAaKSNCGVrYJgJK9r8kG9KU=" + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" + }, + "dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "requires": { + "assert-plus": "^1.0.0" + } + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "deep-eql": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz", + "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==", + "dev": true, + "requires": { + "type-detect": "^4.0.0" + } + }, + "deep-equal": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.0.1.tgz", + "integrity": "sha1-9dJgKStmDghO/0zbyfCK0yR0SLU=" + }, + "deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==" + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" + }, + "delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=" + }, + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" + }, + "destroy": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" + }, + "detect-libc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=" + }, + "diff": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", + "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", + "dev": true + }, + "ecc-jsbn": { + "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.0", + "safer-buffer": "^2.1.0" + } + }, + "ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" + }, + "error-inject": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/error-inject/-/error-inject-1.0.0.tgz", + "integrity": "sha1-4rPZG1Su1nLzCdlQ0VSFD6EdTzc=" + }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" + }, + "eventemitter2": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-2.2.2.tgz", + "integrity": "sha1-QH6nHCAgzVdTggOrfnpr3Pt2ktU=" + }, + "extend": { + "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", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" + }, + "fast-deep-equal": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", + "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=" + }, + "fast-json-stable-stringify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", + "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=" + }, + "fill-keys": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/fill-keys/-/fill-keys-1.0.2.tgz", + "integrity": "sha1-mo+jb06K1jTjv2tPPIiCVRRS6yA=", + "dev": true, + "requires": { + "is-object": "~1.0.1", + "merge-descriptors": "~1.0.0" + } + }, + "forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" + }, + "form-data": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.2.tgz", + "integrity": "sha1-SXBJi+YEwgwAXU9cI67NIda0kJk=", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "1.0.6", + "mime-types": "^2.1.12" + } + }, + "fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" + }, + "fs-minipass": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.5.tgz", + "integrity": "sha512-JhBl0skXjUPCFH7x6x61gQxrKyXsxB5gcgePLZCwfyCGGsTISMoIeObbrvVeP6Xmyaudw4TT43qV2Gz+iyd2oQ==", + "requires": { + "minipass": "^2.2.1" + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" + }, + "gauge": { + "version": "2.7.4", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", + "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", + "requires": { + "aproba": "^1.0.3", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.0", + "object-assign": "^4.1.0", + "signal-exit": "^3.0.0", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wide-align": "^1.1.0" + } + }, + "gekko": { + "version": "0.0.9", + "resolved": "https://registry.npmjs.org/gekko/-/gekko-0.0.9.tgz", + "integrity": "sha1-Q8ot+bv+VC+lntagY8leNVPaVVg=" + }, + "get-func-name": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", + "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=", + "dev": true + }, + "getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "requires": { + "assert-plus": "^1.0.0" + } + }, + "glob": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "growl": { + "version": "1.10.5", + "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", + "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", + "dev": true + }, + "har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=" + }, + "har-validator": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.0.3.tgz", + "integrity": "sha1-ukAsJmGU8VlW7xXg/PJCmT9qff0=", + "requires": { + "ajv": "^5.1.0", + "har-schema": "^2.0.0" + } + }, + "has-ansi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", + "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=" + }, + "he": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz", + "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=", + "dev": true + }, + "http-assert": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/http-assert/-/http-assert-1.3.0.tgz", + "integrity": "sha1-oxpc+IyHPsu1eWkH1NbxMujAHko=", + "requires": { + "deep-equal": "~1.0.1", + "http-errors": "~1.6.1" + }, + "dependencies": { + "http-errors": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", + "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.0", + "statuses": ">= 1.4.0 < 2" + } + } + } + }, + "http-errors": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.0.tgz", + "integrity": "sha512-hz3BtSHB7Z6dNWzYc+gUbWqG4dIpJedwwOhe1cvGUq5tGmcTTIRkPiAbyh/JlZx+ksSJyGJlgcHo5jGahiXnKw==", + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.0", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.0" + } + }, + "http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", + "requires": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + } + }, + "humanize-duration": { + "version": "3.15.1", + "resolved": "https://registry.npmjs.org/humanize-duration/-/humanize-duration-3.15.1.tgz", + "integrity": "sha512-xfwsDoAinTTTNUAuYUGdeSPOUWEXzn9Xkep5LR0gpSw0gMKfpqLA7oxLWVRstYtZUarnCaeN0QqkOTC0TTPUpg==" + }, + "humanize-number": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/humanize-number/-/humanize-number-0.0.2.tgz", + "integrity": "sha1-EcCvakcWQ2M1iFiASPF5lUFInBg=" + }, + "iconv-lite": { + "version": "0.4.23", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz", + "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "ignore-walk": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.1.tgz", + "integrity": "sha512-DTVlMx3IYPe0/JJcYP7Gxg7ttZZu3IInhuEhbchuqneY9wWe5Ojy2mXLBaQFUQmo0AW2r3qG7m1mg86js+gnlQ==", + "requires": { + "minimatch": "^3.0.4" + } + }, + "inflation": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/inflation/-/inflation-2.0.0.tgz", + "integrity": "sha1-i0F+R8KPklpFEz2RTKH9OJEH8w8=" + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + }, + "ini": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", + "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==" + }, + "ipcee": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/ipcee/-/ipcee-1.0.6.tgz", + "integrity": "sha1-PI3I5nh9gdIkyY6POcvvCeBV5tQ=", + "requires": { + "debug": "^2.2.0", + "eventemitter2": "^2.0.0" + } + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "is-object": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-object/-/is-object-1.0.1.tgz", + "integrity": "sha1-iVJojF7C/9awPsyF52ngKQMINHA=", + "dev": true + }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" + }, + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" + }, + "isnumber": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isnumber/-/isnumber-1.0.0.tgz", + "integrity": "sha1-Dj+XWbWB2Z3YUIbw7Cp0kJz63QE=" + }, + "isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" + }, + "jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", + "optional": true + }, + "json-schema": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=" + }, + "json-schema-traverse": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", + "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=" + }, + "json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" + }, + "jsprim": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", + "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", + "requires": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.2.3", + "verror": "1.10.0" + } + }, + "just-extend": { + "version": "1.1.27", + "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-1.1.27.tgz", + "integrity": "sha512-mJVp13Ix6gFo3SBAy9U/kL+oeZqzlYYYLQBwXVBlVzIsZwBqGREnOro24oC/8s8aox+rJhtZ2DiQof++IrkA+g==", + "dev": true + }, + "keygrip": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/keygrip/-/keygrip-1.0.2.tgz", + "integrity": "sha1-rTKXxVcGneqLz+ek+kkbdcXd65E=" + }, + "koa": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/koa/-/koa-1.6.0.tgz", + "integrity": "sha512-tW7xJGDG4LyhFUTtzIyqJCIaJIFgkre1tJPGNe/moRKOIU0L9vEIhW5z7iMX7FJTkYm45urdbPOGBp0VlWF03w==", + "requires": { + "accepts": "^1.2.2", + "co": "^4.4.0", + "composition": "^2.1.1", + "content-disposition": "~0.5.0", + "content-type": "^1.0.0", + "cookies": "~0.7.0", + "debug": "*", + "delegates": "^1.0.0", + "destroy": "^1.0.3", + "error-inject": "~1.0.0", + "escape-html": "~1.0.1", + "fresh": "^0.5.2", + "http-assert": "^1.1.0", + "http-errors": "^1.2.8", + "koa-compose": "^2.3.0", + "koa-is-json": "^1.0.0", + "mime-types": "^2.0.7", + "on-finished": "^2.1.0", + "only": "0.0.2", + "parseurl": "^1.3.0", + "statuses": "^1.2.0", + "type-is": "^1.5.5", + "vary": "^1.0.0" + } + }, + "koa-bodyparser": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/koa-bodyparser/-/koa-bodyparser-2.5.0.tgz", + "integrity": "sha1-PrckP0eZii53LbBfbcTg9PPMvfA=", + "requires": { + "co-body": "^5.1.0", + "copy-to": "^2.0.1" + } + }, + "koa-compose": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/koa-compose/-/koa-compose-2.5.1.tgz", + "integrity": "sha1-cmz7F2lN5cufvwPArfFyMD+D8VY=" + }, + "koa-cors": { + "version": "0.0.16", + "resolved": "https://registry.npmjs.org/koa-cors/-/koa-cors-0.0.16.tgz", + "integrity": "sha1-mBB5k6eQnjTAQphsXsYVbXfzQy4=" + }, + "koa-is-json": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/koa-is-json/-/koa-is-json-1.0.0.tgz", + "integrity": "sha1-JzwH7c3Ljfaiwat9We52SRRR7BQ=" + }, + "koa-logger": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/koa-logger/-/koa-logger-1.3.1.tgz", + "integrity": "sha1-rT9fIZOzM0Mo8+uZphj0sEvui9U=", + "requires": { + "bytes": "1", + "chalk": "^1.1.3", + "humanize-number": "0.0.2", + "passthrough-counter": "^1.0.0" + }, + "dependencies": { + "bytes": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-1.0.0.tgz", + "integrity": "sha1-NWnt6Lo0MV+rmcPpLLBMciDeH6g=" + } + } + }, + "koa-router": { + "version": "5.4.2", + "resolved": "https://registry.npmjs.org/koa-router/-/koa-router-5.4.2.tgz", + "integrity": "sha1-Tb26fnFZU9VobAO3w/29IUYx+HA=", + "requires": { + "co": "^4.6.0", + "debug": "^2.2.0", + "http-errors": "^1.3.1", + "methods": "^1.0.1", + "path-to-regexp": "^1.1.1" + } + }, + "koa-send": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/koa-send/-/koa-send-3.3.0.tgz", + "integrity": "sha1-WkriRVZGgMbs9geeknX6UXOoYdw=", + "requires": { + "co": "^4.6.0", + "debug": "^2.6.0", + "mz": "^2.3.1", + "resolve-path": "^1.3.1" + } + }, + "koa-static": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/koa-static/-/koa-static-2.1.0.tgz", + "integrity": "sha1-z+KS6n2ryWqnI+SkiGFcxlrnQWk=", + "requires": { + "debug": "^2.6.0", + "koa-send": "^3.3.0" + } + }, + "lodash": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz", + "integrity": "sha1-+t2DS5aDBz2hebPq5tnA0VBT9z4=" + }, + "lodash.get": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", + "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=", + "dev": true + }, + "lolex": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/lolex/-/lolex-2.7.1.tgz", + "integrity": "sha512-Oo2Si3RMKV3+lV5MsSWplDQFoTClz/24S0MMHYcgGWWmFXr6TMlqcqk/l1GtH+d5wLBwNRiqGnwDRMirtFalJw==", + "dev": true + }, + "media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" + }, + "merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=", + "dev": true + }, + "methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" + }, + "mime-db": { + "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.19", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.19.tgz", + "integrity": "sha512-P1tKYHVSZ6uFo26mtnve4HQFE3koh1UWVkp8YUC+ESBHe945xWSoXuHHiGarDqcEZ+whpCDnlNw5LON0kLo+sw==", + "requires": { + "mime-db": "~1.35.0" + } + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" + }, + "minipass": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.3.5.tgz", + "integrity": "sha512-Gi1W4k059gyRbyVUZQ4mEqLm0YIUiGYfvxhF6SIlk3ui1WVxMTGfGdQ2SInh3PDrRTVvPKgULkpJtT4RH10+VA==", + "requires": { + "safe-buffer": "^5.1.2", + "yallist": "^3.0.0" + } + }, + "minizlib": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.2.1.tgz", + "integrity": "sha512-7+4oTUOWKg7AuL3vloEWekXY2/D20cevzsrNT2kGWm+39J9hGTCBv8VI5Pm5lXZ/o3/mdR4f8rflAPhnQb8mPA==", + "requires": { + "minipass": "^2.2.1" + } + }, + "mkdirp": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "requires": { + "minimist": "0.0.8" + } + }, + "mocha": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-5.2.0.tgz", + "integrity": "sha512-2IUgKDhc3J7Uug+FxMXuqIyYzH7gJjXECKe/w43IGgQHTSj3InJi+yAA7T24L9bQMRKiUEHxEX37G5JpVUGLcQ==", + "dev": true, + "requires": { + "browser-stdout": "1.3.1", + "commander": "2.15.1", + "debug": "3.1.0", + "diff": "3.5.0", + "escape-string-regexp": "1.0.5", + "glob": "7.1.2", + "growl": "1.10.5", + "he": "1.1.1", + "minimatch": "3.0.4", + "mkdirp": "0.5.1", + "supports-color": "5.4.0" + }, + "dependencies": { + "commander": { + "version": "2.15.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.15.1.tgz", + "integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==", + "dev": true + }, + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "supports-color": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", + "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "module-not-found-error": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/module-not-found-error/-/module-not-found-error-1.0.1.tgz", + "integrity": "sha1-z4tP9PKWQGdNbN0CsOO8UjwrvcA=", + "dev": true + }, + "moment": { + "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=" + }, + "mute-stream": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", + "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=" + }, + "mz": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "requires": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, + "nan": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.10.0.tgz", + "integrity": "sha512-bAdJv7fBLhWC+/Bls0Oza+mvTaNQtP+1RyhhhvD95pgUJz6XM5IzgmxOkItJ9tkoCiplvAnXI1tNmmUD/eScyA==" + }, + "needle": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/needle/-/needle-2.3.1.tgz", + "integrity": "sha512-CaLXV3W8Vnbps8ZANqDGz7j4x7Yj1LW4TWF/TQuDfj7Cfx4nAPTvw98qgTevtto1oHDrh3pQkaODbqupXlsWTg==", + "requires": { + "debug": "^4.1.0", + "iconv-lite": "^0.4.4", + "sax": "^1.2.4" + }, + "dependencies": { + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" + } + } + }, + "negotiator": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz", + "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=" + }, + "nise": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/nise/-/nise-1.4.2.tgz", + "integrity": "sha512-BxH/DxoQYYdhKgVAfqVy4pzXRZELHOIewzoesxpjYvpU+7YOalQhGNPf7wAx8pLrTNPrHRDlLOkAl8UI0ZpXjw==", + "dev": true, + "requires": { + "@sinonjs/formatio": "^2.0.0", + "just-extend": "^1.1.27", + "lolex": "^2.3.2", + "path-to-regexp": "^1.7.0", + "text-encoding": "^0.6.4" + } + }, + "node-pre-gyp": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.11.0.tgz", + "integrity": "sha512-TwWAOZb0j7e9eGaf9esRx3ZcLaE5tQ2lvYy1pb5IAaG1a2e2Kv5Lms1Y4hpj+ciXJRofIxxlt5haeQ/2ANeE0Q==", + "requires": { + "detect-libc": "^1.0.2", + "mkdirp": "^0.5.1", + "needle": "^2.2.1", + "nopt": "^4.0.1", + "npm-packlist": "^1.1.6", + "npmlog": "^4.0.2", + "rc": "^1.2.7", + "rimraf": "^2.6.1", + "semver": "^5.3.0", + "tar": "^4" + } + }, + "nodegit-promise": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/nodegit-promise/-/nodegit-promise-4.0.0.tgz", + "integrity": "sha1-VyKxhPLfcycWEGSnkdLoQskWezQ=", + "requires": { + "asap": "~2.0.3" + } + }, + "nopt": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.1.tgz", + "integrity": "sha1-0NRoWv1UFRk8jHUFYC0NF81kR00=", + "requires": { + "abbrev": "1", + "osenv": "^0.1.4" + } + }, + "npm-bundled": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.0.6.tgz", + "integrity": "sha512-8/JCaftHwbd//k6y2rEWp6k1wxVfpFzB6t1p825+cUb7Ym2XQfhwIC5KwhrvzZRJu+LtDE585zVaS32+CGtf0g==" + }, + "npm-packlist": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.4.1.tgz", + "integrity": "sha512-+TcdO7HJJ8peiiYhvPxsEDhF3PJFGUGRcFsGve3vxvxdcpO2Z4Z7rkosRM0kWj6LfbK/P0gu3dzk5RU1ffvFcw==", + "requires": { + "ignore-walk": "^3.0.1", + "npm-bundled": "^1.0.1" + } + }, + "npmlog": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", + "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", + "requires": { + "are-we-there-yet": "~1.1.2", + "console-control-strings": "~1.1.0", + "gauge": "~2.7.3", + "set-blocking": "~2.0.0" + } + }, + "number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" + }, + "oauth-sign": { + "version": "0.8.2", + "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=" + }, + "on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "requires": { + "ee-first": "1.1.1" + } + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "requires": { + "wrappy": "1" + } + }, + "only": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/only/-/only-0.0.2.tgz", + "integrity": "sha1-Kv3oTQPlC5qO3EROMGEKcCle37Q=" + }, + "opn": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/opn/-/opn-4.0.2.tgz", + "integrity": "sha1-erwi5kTf9jsKltWrfyeQwPAavJU=", + "requires": { + "object-assign": "^4.0.1", + "pinkie-promise": "^2.0.0" + } + }, + "os-homedir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", + "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=" + }, + "os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=" + }, + "osenv": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", + "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", + "requires": { + "os-homedir": "^1.0.0", + "os-tmpdir": "^1.0.0" + } + }, + "parseurl": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz", + "integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M=" + }, + "passthrough-counter": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/passthrough-counter/-/passthrough-counter-1.0.0.tgz", + "integrity": "sha1-GWfZ5m2lcrXAI8eH2xEqOHqxZvo=" + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" + }, + "path-to-regexp": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.7.0.tgz", + "integrity": "sha1-Wf3g9DW62suhA6hOnTvGTpa5k30=", + "requires": { + "isarray": "0.0.1" + } + }, + "pathval": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.0.tgz", + "integrity": "sha1-uULm1L3mUwBe9rcTYd74cn0GReA=", + "dev": true + }, + "performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" + }, + "pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=" + }, + "pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", + "requires": { + "pinkie": "^2.0.0" + } + }, + "process-nextick-args": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", + "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==" + }, + "promisify-node": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/promisify-node/-/promisify-node-0.5.0.tgz", + "integrity": "sha512-GR2E4qgCoKFTprhULqP2OP3bl8kHo16XtnqtkHH6be7tPW1yL6rXd15nl3oV2sLTFv1+j6tqoF69VVpFtJ/j+A==", + "requires": { + "nodegit-promise": "^4.0.0", + "object-assign": "^4.1.1" + } + }, + "prompt-lite": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/prompt-lite/-/prompt-lite-0.1.1.tgz", + "integrity": "sha1-+oSPgboTnn0/Mhdz3a/JPTKFfHU=", + "requires": { + "async": "~0.1.22", + "colors": "0.6.x", + "read": "1.0.x", + "revalidator": "0.1.x" + }, + "dependencies": { + "async": { + "version": "0.1.22", + "resolved": "https://registry.npmjs.org/async/-/async-0.1.22.tgz", + "integrity": "sha1-D8GqoIig4+8Ovi2IMbqw3PiEUGE=" + } + } + }, + "proxyquire": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/proxyquire/-/proxyquire-1.8.0.tgz", + "integrity": "sha1-AtUUpb7ZhvBMuyCTrxZ0FTX3ntw=", + "dev": true, + "requires": { + "fill-keys": "^1.0.2", + "module-not-found-error": "^1.0.0", + "resolve": "~1.1.7" + } + }, + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" + }, + "qs": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" + }, + "raw-body": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.3.tgz", + "integrity": "sha512-9esiElv1BrZoI3rCDuOuKCBRbuApGGaDPQfjSflGxdy4oyzqghxu6klEkkVIvBje+FF0BX9coEv8KqW6X/7njw==", + "requires": { + "bytes": "3.0.0", + "http-errors": "1.6.3", + "iconv-lite": "0.4.23", + "unpipe": "1.0.0" + }, + "dependencies": { + "http-errors": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", + "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.0", + "statuses": ">= 1.4.0 < 2" + } + } + } + }, + "rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "requires": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" + } + } + }, + "read": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/read/-/read-1.0.7.tgz", + "integrity": "sha1-s9oZvQUkMal2cdRKQmNK33ELQMQ=", + "requires": { + "mute-stream": "~0.0.4" + } + }, + "readable-stream": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + } + } + }, + "relieve": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/relieve/-/relieve-2.2.3.tgz", + "integrity": "sha512-h3i6n981nqh1K0TxK+TKaFf/Kf2MUgJHhEYUeKNj+by0uoxne/ldYsNKB2HA9fBX77v+uZE+pfxoRa9ZxuBgIA==", + "requires": { + "bluebird": "^3.4.0", + "debug": "^2.2.0", + "eventemitter2": "^2.0.0", + "ipcee": "^1.0.6", + "uuid": "^2.0.2" + }, + "dependencies": { + "uuid": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-2.0.3.tgz", + "integrity": "sha1-Z+LoY3lyFVMN/zGOW/nc6/1Hsho=" + } + } + }, + "request": { + "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.6.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.5", + "extend": "~3.0.1", + "forever-agent": "~0.6.1", + "form-data": "~2.3.1", + "har-validator": "~5.0.3", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.17", + "oauth-sign": "~0.8.2", + "performance-now": "^2.1.0", + "qs": "~6.5.1", + "safe-buffer": "^5.1.1", + "tough-cookie": "~2.3.3", + "tunnel-agent": "^0.6.0", + "uuid": "^3.1.0" + } + }, + "request-promise": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/request-promise/-/request-promise-4.2.2.tgz", + "integrity": "sha1-0epG1lSm7k+O5qT+oQGMIpEZBLQ=", + "requires": { + "bluebird": "^3.5.0", + "request-promise-core": "1.1.1", + "stealthy-require": "^1.1.0", + "tough-cookie": ">=2.3.3" + } + }, + "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.13.1" + }, + "dependencies": { + "lodash": { + "version": "4.17.10", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.10.tgz", + "integrity": "sha512-UejweD1pDoXu+AD825lWwp4ZGtSwgnpZxb3JDViD7StjQz+Nb/6l093lx4OQ0foGWNRoc19mWy7BzL+UAK2iVg==" + } + } + }, + "resolve": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", + "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=", + "dev": true + }, + "resolve-path": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/resolve-path/-/resolve-path-1.4.0.tgz", + "integrity": "sha1-xL2p9e+y/OZSR4c6s2u02DT+Fvc=", + "requires": { + "http-errors": "~1.6.2", + "path-is-absolute": "1.0.1" + }, + "dependencies": { + "http-errors": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", + "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.0", + "statuses": ">= 1.4.0 < 2" + } + } + } + }, + "retry": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.10.1.tgz", + "integrity": "sha1-52OI0heZLCUnUCQdPTlW/tmNj/Q=" + }, + "revalidator": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/revalidator/-/revalidator-0.1.8.tgz", + "integrity": "sha1-/s5hv6DBtSoga9axgZgYS91SOjs=" + }, + "rimraf": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", + "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", + "requires": { + "glob": "^7.1.3" + }, + "dependencies": { + "glob": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", + "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + } + } + }, + "safe-buffer": { + "version": "5.1.2", + "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==" + }, + "samsam": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/samsam/-/samsam-1.3.0.tgz", + "integrity": "sha512-1HwIYD/8UlOtFS3QO3w7ey+SdSDFE4HRNLZoZRYVQefrOY3l17epswImeB1ijgJFQJodIaHcwkp3r/myBjFVbg==", + "dev": true + }, + "sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" + }, + "semver": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.4.1.tgz", + "integrity": "sha512-WfG/X9+oATh81XtllIo/I8gOiY9EXRdv1cQdyykeXK17YcUW3EXUAi2To4pcH6nZtJPr7ZOpM5OMyWJZm+8Rsg==" + }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" + }, + "setprototypeof": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", + "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==" + }, + "signal-exit": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=" + }, + "sinon": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-4.5.0.tgz", + "integrity": "sha512-trdx+mB0VBBgoYucy6a9L7/jfQOmvGeaKZT4OOJ+lPAtI8623xyGr8wLiE4eojzBS8G9yXbhx42GHUOVLr4X2w==", + "dev": true, + "requires": { + "@sinonjs/formatio": "^2.0.0", + "diff": "^3.1.0", + "lodash.get": "^4.4.2", + "lolex": "^2.2.0", + "nise": "^1.2.0", + "supports-color": "^5.1.0", + "type-detect": "^4.0.5" + }, + "dependencies": { + "supports-color": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", + "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "sqlite3": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/sqlite3/-/sqlite3-4.0.6.tgz", + "integrity": "sha512-EqBXxHdKiwvNMRCgml86VTL5TK1i0IKiumnfxykX0gh6H6jaKijAXvE9O1N7+omfNSawR2fOmIyJZcfe8HYWpw==", + "requires": { + "nan": "~2.10.0", + "node-pre-gyp": "^0.11.0", + "request": "^2.87.0" + } + }, + "sshpk": { + "version": "1.14.2", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.14.2.tgz", + "integrity": "sha1-xvxhZIo9nE52T9P8306hBeSSupg=", + "requires": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + } + }, + "stats-lite": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/stats-lite/-/stats-lite-2.1.1.tgz", + "integrity": "sha512-5QkxGCWGMbeQ+PXqI2N7ES6kW4IimvbMQBCKvZbekaEpf3InckVHiIXdCJbZsKUjLE7a3jha2cTEJqtOGGcVMw==", + "requires": { + "isnumber": "~1.0.0" + } + }, + "statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" + }, + "stealthy-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz", + "integrity": "sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks=" + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=" + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=" + }, + "tar": { + "version": "4.4.8", + "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.8.tgz", + "integrity": "sha512-LzHF64s5chPQQS0IYBn9IN5h3i98c12bo4NCO7e0sGM2llXQ3p2FGC5sdENN4cTW48O915Sh+x+EXx7XW96xYQ==", + "requires": { + "chownr": "^1.1.1", + "fs-minipass": "^1.2.5", + "minipass": "^2.3.4", + "minizlib": "^1.1.1", + "mkdirp": "^0.5.0", + "safe-buffer": "^5.1.2", + "yallist": "^3.0.2" + } + }, + "text-encoding": { + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/text-encoding/-/text-encoding-0.6.4.tgz", + "integrity": "sha1-45mpgiV6J22uQou5KEXLcb3CbRk=", + "dev": true + }, + "thenify": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.0.tgz", + "integrity": "sha1-5p44obq+lpsBCCB5eLn2K4hgSDk=", + "requires": { + "any-promise": "^1.0.0" + } + }, + "thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha1-GhkY1ALY/D+Y+/I02wvMjMEOlyY=", + "requires": { + "thenify": ">= 3.1.0 < 4" + } + }, + "thunkify": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/thunkify/-/thunkify-0.0.1.tgz", + "integrity": "sha1-vV02sQabQHjl3LrI+rRTWb6mGD0=" + }, + "tiny-promisify": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/tiny-promisify/-/tiny-promisify-0.1.1.tgz", + "integrity": "sha1-FoHhWVyKEUIq9b9ESAIVQCdiq4Q=" + }, + "toidentifier": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", + "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==" + }, + "toml": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/toml/-/toml-2.3.3.tgz", + "integrity": "sha512-O7L5hhSQHxuufWUdcTRPfuTh3phKfAZ/dqfxZFoxPCj2RYmpaSGLEIs016FCXItQwNr08yefUB5TSjzRYnajTA==" + }, + "tough-cookie": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.4.tgz", + "integrity": "sha512-TZ6TTfI5NtZnuyy/Kecv+CnoROnyXn2DN97LontgQpCwsX2XyLYCC0ENhYkehSOwAp8rTQKc/NUIF7BkQ5rKLA==", + "requires": { + "punycode": "^1.4.1" + } + }, + "tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "requires": { + "safe-buffer": "^5.0.1" + } + }, + "tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", + "optional": true + }, + "type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true + }, + "type-is": { + "version": "1.6.16", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.16.tgz", + "integrity": "sha512-HRkVv/5qY2G6I8iab9cI7v1bOIdhm94dVjQCPFElW9W+3GeDOSHmy2EBYe4VTApuzolPcmgFTN3ftVJRKR2J9Q==", + "requires": { + "media-typer": "0.3.0", + "mime-types": "~2.1.18" + } + }, + "ultron": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ultron/-/ultron-1.1.1.tgz", + "integrity": "sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og==" + }, + "unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + }, + "uuid": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", + "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==" + }, + "vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" + }, + "verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "requires": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } + }, + "wide-align": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", + "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", + "requires": { + "string-width": "^1.0.2 || 2" + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + }, + "ws": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-6.0.0.tgz", + "integrity": "sha512-c2UlYcAZp1VS8AORtpq6y4RJIkJ9dQz18W32SpR/qXGfLDZ2jU4y4wKvvZwqbi7U6gxFQTeE+urMbXU/tsDy4w==", + "requires": { + "async-limiter": "~1.0.0" + } + }, + "yallist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.3.tgz", + "integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==" + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..d24739e --- /dev/null +++ b/package.json @@ -0,0 +1,60 @@ +{ + "name": "gekko", + "version": "0.6.8", + "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": { + "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", + "relieve": "^2.1.3", + "retry": "^0.10.1", + "semver": "5.4.1", + "sqlite3": "^4.0.6", + "stats-lite": "^2.0.4", + "tiny-promisify": "^0.1.1", + "toml": "^2.3.0", + "ws": "^6.0.0" + }, + "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" + } +} diff --git a/plugins.js b/plugins.js new file mode 100644 index 0000000..4404305 --- /dev/null +++ b/plugins.js @@ -0,0 +1,260 @@ +// All plugins supported by Gekko. +// +// Required parameters per plugin. +// +// name: Name of the plugin +// slug: name of the plugin mapped to the config key. Expected +// filename to exist in `gekko/plugins/` (only if path is not +// specified) +// async: upon creating a new plugin instance, does something async +// happen where Gekko needs to wait for? If set to true, the +// constructor will be passed a callback which it should execute +// as soon as Gekko can continue. +// modes: a list indicating in what Gekko modes this plugin is +// allowed to run. Realtime is during a live market watch and +// backtest is during a backtest. +// +// +// Optional parameters per plugin. +// +// description: text describing the plugin. +// dependencies: a list of external npm modules this plugin requires to +// be installed. +// emits: events emitted by this plugin that other plugins can subscribe to. +// path: fn that returns path of file of the plugin (overwrites `gekko/plugins/{slug}`) +// when given the configuration object (relative from `gekko/plugins/`). +// greedy: if this plugin wants to subscribe to a lot of events, but can function +// properly when some events wont be emitted. +var plugins = [ + { + name: 'Candle writer', + description: 'Store candles in a database', + slug: 'candleWriter', + async: true, + modes: ['realtime', 'importer'], + path: config => config.adapter + '/writer', + version: 0.1, + }, + { + name: 'Trading Advisor', + description: 'Calculate trading advice', + slug: 'tradingAdvisor', + async: true, + modes: ['realtime', 'backtest'], + emits: true, + path: config => 'tradingAdvisor/tradingAdvisor.js', + }, + { + name: 'IRC bot', + description: 'IRC module lets you communicate with Gekko on IRC.', + slug: 'ircbot', + async: false, + modes: ['realtime'], + dependencies: [{ + module: 'irc', + version: '0.5.2' + }] + }, + { + name: 'Telegram bot', + description: 'Telegram module lets you communicate with Gekko on Telegram.', + slug: 'telegrambot', + async: false, + modes: ['realtime'], + dependencies: [{ + module: 'node-telegram-bot-api', + version: '0.24.0' + }] + }, + { + name: 'XMPP bot', + description: 'XMPP module lets you communicate with Gekko on Jabber.', + slug: 'xmppbot', + async: false, + silent: false, + modes: ['realtime'], + dependencies: [{ + module: 'node-xmpp-client', + version: '3.0.2' + }] + }, + { + name: 'Pushover', + description: 'Sends pushover.', + slug: 'pushover', + async: false, + modes: ['realtime'], + dependencies: [{ + module: 'pushover-notifications', + version: '0.2.3' + }] + }, + { + name: 'Campfire bot', + description: 'Lets you communicate with Gekko on Campfire.', + slug: 'campfire', + async: false, + modes: ['realtime'], + dependencies: [{ + module: 'ranger', + version: '0.2.4' + }] + }, + { + name: 'Mailer', + description: 'Sends you an email everytime Gekko has new advice.', + slug: 'mailer', + async: true, + modes: ['realtime'], + dependencies: [{ + module: 'emailjs', + version: '1.0.5' + }, { + module: 'prompt-lite', + version: '0.1.1' + }] + }, + { + name: 'Advice logger', + description: '', + slug: 'adviceLogger', + async: false, + silent: true, + modes: ['realtime'] + }, + { + name: 'Trader', + description: 'Follows the advice and create real orders.', + slug: 'trader', + async: true, + modes: ['realtime'], + emits: true, + path: config => 'trader/trader.js', + }, + { + name: 'Paper Trader', + description: 'Paper trader that simulates fake trades.', + slug: 'paperTrader', + async: false, + modes: ['realtime', 'backtest'], + emits: true, + path: config => 'paperTrader/paperTrader.js', + }, + { + name: 'Performance Analyzer', + description: 'Analyzes performances of trades', + slug: 'performanceAnalyzer', + async: false, + modes: ['realtime', 'backtest'], + emits: true, + path: config => 'performanceAnalyzer/performanceAnalyzer.js', + }, + { + name: 'Redis beacon', + slug: 'redisBeacon', + description: 'Publish events over Redis Pub/Sub', + async: true, + modes: ['realtime'], + dependencies: [{ + module: 'redis', + version: '0.10.0' + }] + }, + { + name: 'Pushbullet', + description: 'Sends advice to pushbullet.', + slug: 'pushbullet', + async: false, + modes: ['realtime'], + dependencies: [{ + module: 'pushbullet', + version: '1.4.3' + }] + }, + { + name: 'Kodi', + description: 'Sends advice to Kodi.', + slug: 'kodi', + async: false, + modes: ['realtime'] + }, + { + name: 'Candle Uploader', + description: 'Upload candles to an extneral server', + slug: 'candleUploader', + async: true, + modes: ['realtime'] + }, + { + name: 'Twitter', + description: 'Sends trades to twitter.', + slug: 'twitter', + async: false, + modes: ['realtime'], + dependencies: [{ + module: 'twitter', + version: '1.7.1' + }] + }, + { + name: 'Slack', + description: 'Sends trades to slack channel.', + slug: 'slack', + async: false, + modes: ['realtime'], + dependencies: [{ + module: '@slack/client', + version: '3.13.0' + }] + }, + { + name: 'IFTTT', + description: 'Sends trades to IFTTT webhook.', + slug: 'ifttt', + async: false, + modes: ['realtime'] + }, + { + name: 'Event logger', + description: 'Logs all gekko events.', + slug: 'eventLogger', + async: false, + modes: ['realtime', 'backtest'], + greedy: true + }, + { + name: 'Backtest result export', + description: 'Exports the results of a gekko backtest', + slug: 'backtestResultExporter', + async: false, + modes: ['backtest'] + }, + { + name: 'Child to parent', + description: 'Relays events from the child to the parent process', + slug: 'childToParent', + async: false, + modes: ['realtime'], + greedy: true + }, + { + name: 'Candle Uploader', + description: 'Upload realtime market candles to an external server', + slug: 'candleUploader', + async: true, + modes: ['realtime'], + dependencies: [{ + module: 'axios', + version: '0.18.0' + }] + }, + { + name: 'Blotter', + description: 'Writes all buy/sell trades to a blotter CSV file', + slug: 'blotter', + async: false, + modes: ['realtime'], + }, +]; + +module.exports = plugins; diff --git a/plugins/adviceLogger.js b/plugins/adviceLogger.js new file mode 100644 index 0000000..fb5a76a --- /dev/null +++ b/plugins/adviceLogger.js @@ -0,0 +1,36 @@ +var log = require('../core/log'); +var moment = require('moment'); +var _ = require('lodash'); +var util = require('../core/util.js'); +var config = util.getConfig(); +var adviceLoggerConfig = config.adviceLogger; + +var Actor = function() { + this.price = 'N/A'; + this.marketTime = {format: function() {return 'N/A'}}; + _.bindAll(this); +} + +Actor.prototype.processCandle = function(candle, done) { + this.price = candle.close; + this.marketTime = candle.start; + + done(); +}; + +Actor.prototype.processAdvice = function(advice) { + if (adviceLoggerConfig.muteSoft && advice.recommendation == 'soft') return; + console.log() + log.info('We have new trading advice!'); + log.info('\t Position:', advice.recommendation); + log.info('\t Market price:', this.price); + log.info('\t Based on market time:', this.marketTime.format('YYYY-MM-DD HH:mm:ss')); + console.log() +}; + +Actor.prototype.finalize = function(advice, done) { + // todo + done(); +}; + +module.exports = Actor; diff --git a/plugins/backtestResultExporter.js b/plugins/backtestResultExporter.js new file mode 100644 index 0000000..21dc555 --- /dev/null +++ b/plugins/backtestResultExporter.js @@ -0,0 +1,146 @@ +// Small plugin that subscribes to some events, stores +// them and sends it to the parent process. + +const log = require('../core/log'); +const _ = require('lodash'); +const util = require('../core/util.js'); +const env = util.gekkoEnv(); +const config = util.getConfig(); +const moment = require('moment'); +const fs = require('fs'); + +const BacktestResultExporter = function() { + this.performanceReport; + this.roundtrips = []; + this.stratUpdates = []; + this.stratCandles = []; + this.trades = []; + + this.candleProps = config.backtestResultExporter.data.stratCandleProps; + + if(!config.backtestResultExporter.data.stratUpdates) + this.processStratUpdate = null; + + if(!config.backtestResultExporter.data.roundtrips) + this.processRoundtrip = null; + + if(!config.backtestResultExporter.data.stratCandles) + this.processStratCandles = null; + + if(!config.backtestResultExporter.data.portfolioValues) + this.processPortfolioValueChange = null; + + if(!config.backtestResultExporter.data.trades) + this.processTradeCompleted = null; + + _.bindAll(this); +} + +BacktestResultExporter.prototype.processPortfolioValueChange = function(portfolio) { + this.portfolioValue = portfolio.balance; +} + +BacktestResultExporter.prototype.processStratCandle = function(candle) { + let strippedCandle; + + if(!this.candleProps) { + strippedCandle = { + ...candle, + start: candle.start.unix() + } + } else { + strippedCandle = { + ..._.pick(candle, this.candleProps), + start: candle.start.unix() + } + } + + if(config.backtestResultExporter.data.portfolioValues) + strippedCandle.portfolioValue = this.portfolioValue; + + this.stratCandles.push(strippedCandle); +}; + +BacktestResultExporter.prototype.processRoundtrip = function(roundtrip) { + this.roundtrips.push({ + ...roundtrip, + entryAt: roundtrip.entryAt.unix(), + exitAt: roundtrip.exitAt.unix() + }); +}; + +BacktestResultExporter.prototype.processTradeCompleted = function(trade) { + this.trades.push({ + ...trade, + date: trade.date.unix() + }); +}; + +BacktestResultExporter.prototype.processStratUpdate = function(stratUpdate) { + this.stratUpdates.push({ + ...stratUpdate, + date: stratUpdate.date.unix() + }); +} + +BacktestResultExporter.prototype.processPerformanceReport = function(performanceReport) { + this.performanceReport = performanceReport; +} + +BacktestResultExporter.prototype.finalize = function(done) { + const backtest = { + market: config.watch, + tradingAdvisor: config.tradingAdvisor, + strategyParameters: config[config.tradingAdvisor.method], + performanceReport: this.performanceReport + }; + + if(config.backtestResultExporter.data.stratUpdates) + backtest.stratUpdates = this.stratUpdates; + + if(config.backtestResultExporter.data.roundtrips) + backtest.roundtrips = this.roundtrips; + + if(config.backtestResultExporter.data.stratCandles) + backtest.stratCandles = this.stratCandles; + + if(config.backtestResultExporter.data.trades) + backtest.trades = this.trades; + + if(env === 'child-process') { + process.send({backtest}); + } + + if(config.backtestResultExporter.writeToDisk) { + this.writeToDisk(backtest, done); + } else { + done(); + } +}; + +BacktestResultExporter.prototype.writeToDisk = function(backtest, next) { + let filename; + + if(config.backtestResultExporter.filename) { + filename = config.backtestResultExporter.filename; + } else { + const now = moment().format('YYYY-MM-DD_HH-mm-ss'); + filename = `backtest-${config.tradingAdvisor.method}-${now}.json`; + } + + fs.writeFile( + util.dirs().gekko + filename, + JSON.stringify(backtest), + err => { + if(err) { + log.error('unable to write backtest result', err); + } else { + log.info('written backtest to: ', util.dirs().gekko + filename); + } + + next(); + } + ); +} + +module.exports = BacktestResultExporter; diff --git a/plugins/blotter.js b/plugins/blotter.js new file mode 100644 index 0000000..a67c6a3 --- /dev/null +++ b/plugins/blotter.js @@ -0,0 +1,118 @@ +const fsw = require('fs'); +const _ = require('lodash'); +const log = require('../core/log.js'); +const util = require('../core/util.js'); +const config = util.getConfig(); +const blotterConfig = config.blotter; + +var Blotter = function(done) { + _.bindAll(this); + + this.time; + this.valueAtBuy = 0.0; + this.filename = blotterConfig.filename; + this.dateformat = blotterConfig.dateFormat; + this.timezone = blotterConfig.timezone; + this.headertxt = ''; + this.outtxt = ''; + this.tradeError = false; + this.inaccurateData = false; + + this.done = done; + this.setup(); +}; + +Blotter.prototype.setup = function(done) { + + this.headertxt = "Date,Price,Amount,Side,Fees,Value,P&L,Notes\n"; + + fsw.readFile(this.filename, (err, _) => { + if (err) { + log.warn('No file with the name', this.filename, 'found. Creating new blotter file'); + fsw.appendFile(this.filename, this.headertxt, 'utf8', (err) => { + if(err) { + log.error('Unable to write header text to blotter'); + } + }); + } + }); + +}; + +Blotter.prototype.processTradeCompleted = function(trade) { + // if exchange doesn't send correct timezone, correct it + if (trade.date.format('Z') == '+00:00') { + var adjustTimezone = trade.date.utcOffset(this.timezone); + this.time = adjustTimezone.format(this.dateformat); + } else { + this.time = trade.date.format(this.dateformat); + } + // If a trade date is from 1969 or 1970, there was an error with the trade + if (trade.date.format('YY') == '69' || trade.date.format('YY') == '70') { + log.error('Received 1969/1970 error, trade failed to execute, did not record in blotter'); + // Prevent roundTrip from writing error P&L in processRoundtrip method + if (trade.action == 'sell') { + this.tradeError = true; + } + return; + } + + this.valueAtBuy = this.roundUp(trade.effectivePrice * trade.amount); + + if (trade.action === 'buy') { + //time, price, amount, side, fees, value at buy + this.outtxt = this.time + "," + trade.effectivePrice.toFixed(2) + "," + trade.amount.toFixed(8) + "," + trade.action + "," + trade.feePercent + "," + this.valueAtBuy; + if ((trade.price == 0 || isNaN(trade.price)) && (trade.amount == 0|| isNaN(trade.amount))) { + this.outtxt = this.outtxt + "," + ",Trade probably went through but didn't receive correct price/amount info\n"; + } else { + this.outtxt = this.outtxt + "\n"; + } + this.writeBlotter(); + return; + } + else if (trade.action === 'sell'){ + var sellValue = (this.roundUp(trade.effectivePrice * trade.amount)); + //time, price, amount, side, fees, value at sell + this.outtxt = this.time + "," + trade.effectivePrice.toFixed(2) + "," + trade.amount.toFixed(8) + "," + trade.action + "," + trade.feePercent + "," + sellValue + ","; + if ((trade.price == 0 || isNaN(trade.price)) && (trade.amount == 0|| isNaN(trade.amount))) { + this.inaccurateData = true; + } + this.valueAtBuy = 0.0; + // wait til processRoundtrip complete to write to file + } + +} + +Blotter.prototype.writeBlotter = function() { + fsw.appendFile(this.filename, this.outtxt, 'utf8', (err) => { + if(err) { + log.error('Unable to write trade to blotter'); + } + }); +} + +Blotter.prototype.processRoundtrip = function(trip) { + log.info('Roundtrip', trip); + if (!this.tradeError) { + this.outtxt = this.outtxt + this.roundUp(trip.pnl) +'\n'; + this.writeBlotter(); + return; + } + if (this.inaccurateData) { + this.outtxt = this.outtxt + this.roundUp(trip.pnl) + ",Trade probably went through but didn't receive correct price/amount info\n"; + this.inaccurateData = false; + this.writeBlotter; + return; + } + + // 1969/1970 sell trade error not written to blotter, resetting flag to false + this.tradeError = false; +} + +Blotter.prototype.roundUp = function(value) { + var cents = value * 100; + var roundedCents = Math.round(cents); + return roundedCents / 100; +} + +module.exports = Blotter; diff --git a/plugins/campfire.js b/plugins/campfire.js new file mode 100644 index 0000000..244be85 --- /dev/null +++ b/plugins/campfire.js @@ -0,0 +1,132 @@ +var _ = require('lodash'); +var Moment = require('moment'); +var Ranger = require('ranger'); + +var config = require('../core/util').getConfig().campfire; + +var Actor = function() { + _.bindAll(this); + + this.commands = [{ + 'handler': 'advice', + 'callback': this.sayAdvice, + 'description': "Advice on what position to take, depending on the current trend" + }, { + 'handler': 'price', + 'callback': this.sayPrice, + 'description': "The current price of the asset in the configured currency" + }, { + 'handler': 'donate', + 'callback': this.sayDonate, + 'description': "Where to send all of that extra coin that's weighing you down" + }, { + 'handler': 'help', + 'callback': this.sayHelp, + 'description': "You are here" + }]; + + this.advice = null; + this.adviceTime = Moment.utc(); + this.price = null; + this.priceTime = Moment.utc(); + + this.client = Ranger.createClient(config.account, config.apiKey); + this.client.room(config.roomId, this.joinRoom); + this.client.me(this.whoAmI); +}; + +Actor.prototype = { + processCandle: function(candle, done) { + this.price = candle.close; + this.priceTime = candle.date.start(); + + done(); + }, + + processAdvice: function(advice) { + if (campfire.muteSoft && advice.recommendation === 'soft') return; + this.advice = advice.recommendation; + this.adviceTime = Moment.utc(); + + if (config.emitUpdates) { + this.sayAdvice(); + } + }, + + sayAdvice: function() { + var message; + + if (this.advice !== null) { + message = ["We think you should", this.advice + ".", "(" + this.adviceTime.fromNow() + ")"]; + } else { + message = ["We don't have any advice for you quite yet."]; + } + + this.room.speak(message.join(' ')); + }, + + sayPrice: function() { + var message; + + if (this.price !== null) { + message = ["The price at the moment is", this.price + ".", "(" + this.priceTime.fromNow() + ")"]; + } else { + message = ["We don't know the price right now."]; + } + + this.room.speak(message.join(' ')); + }, + + sayDonate: function() { + this.room.speak("If you'd like to donate a few coins, you can send them here: 13r1jyivitShUiv9FJvjLH7Nh1ZZptumwW"); + }, + + sayHelp: function() { + this.room.speak("I listen for the following inquiries...", this.pasteDescriptions); + }, + + pasteDescriptions: function() { + var descriptions = _.map(this.commands, function(command) { + return [command.handler + ':', command.description].join(' '); + }, this).join('\n'); + + this.room.paste(descriptions); + }, + + joinRoom: function(room) { + this.room = room; + this.room.join(this.listenForCommands); + }, + + listenForCommands: function() { + this.room.listen(this.executeCommands); + }, + + executeCommands: function(message) { + if (message.userId === this.user.id) return false; // Make the bot ignore itself + if (message.body === null) return false; // Handle weird cases where body is null sometimes + + _.each(this.commands, function(command) { + if (this.textHasCommandForBot(message.body, config.nickname, command.handler)) { + command.callback(); + } + }, this); + }, + + textHasCommandForBot: function(text, nickname, handler) { + var normalizedText = text.toLowerCase(), + normalizedNickname = nickname.toLowerCase(), + normalizedHandler = handler.toLowerCase(); + + var nicknameWasMentioned = normalizedText.indexOf(normalizedNickname) >= 0, + handlerWasMentioned = normalizedText.indexOf(normalizedHandler) >= 0; + + return nicknameWasMentioned && handlerWasMentioned; + }, + + whoAmI: function(user) { + this.user = user; + } +}; + +module.exports = Actor; diff --git a/plugins/candleUploader.js b/plugins/candleUploader.js new file mode 100644 index 0000000..388f56d --- /dev/null +++ b/plugins/candleUploader.js @@ -0,0 +1,77 @@ +const axios = require('axios'); +const _ = require('lodash'); +const log = require('../core/log.js'); +const util = require('../core/util.js'); +const config = util.getConfig(); + +const CandleUploader = function(done) { + _.bindAll(this); + + done(); + this.candles = []; + this.schedule(); +}; + +CandleUploader.prototype.processCandle = function(candle, done) { + this.candles.push(candle); + done(); +}; + +CandleUploader.prototype.schedule = function() { + this.timer = setTimeout(this.upload, 10 * 1000); +} + +CandleUploader.prototype.rawUpload = function(candles, count, next) { + + const amount = candles.length; + + axios({ + url: config.candleUploader.url, + method: 'post', + data: { + apiKey: config.candleUploader.apiKey, + watch: config.watch, + candles: candles + } + }) + .then(r => { + if(r.data.success === false) { + console.log('error uploading:', r.data); + } + console.log(new Date, 'uploaded', amount, 'candles'); + + next(); + }) + .catch(e => { + console.log('error uploading:', e.message); + + count++; + + if(count > 10) { + console.log('FINAL error uploading:', e.message); + return next(); + } + + setTimeout(() => this.rawUpload(candles, count, next), 2000); + }); +} + +CandleUploader.prototype.upload = function() { + const amount = this.candles.length; + if(!amount) { + return this.schedule(); + } + + this.rawUpload(this.candles, 0, () => { + this.schedule(); + }); + + this.candles = []; +} + +CandleUploader.prototype.finish = function(next) { + this.upload(); + clearTimeout(this.timer); +} + +module.exports = CandleUploader; diff --git a/plugins/childToParent.js b/plugins/childToParent.js new file mode 100644 index 0000000..2044c61 --- /dev/null +++ b/plugins/childToParent.js @@ -0,0 +1,24 @@ +// Small plugin that subscribes to some events, stores +// them and sends it to the parent process. + +const log = require('../core/log'); +const _ = require('lodash'); +const subscriptions = require('../subscriptions'); +const config = require('../core/util').getConfig(); + +const ChildToParent = function() { + + subscriptions + // .filter(sub => config.childToParent.events.includes(sub.event)) + .forEach(sub => { + this[sub.handler] = (event, next) => { + process.send({type: sub.event, payload: event}); + if(_.isFunction(next)) { + next(); + } + } + }, this); + +} + +module.exports = ChildToParent; \ No newline at end of file diff --git a/plugins/eventLogger.js b/plugins/eventLogger.js new file mode 100644 index 0000000..6e07fe0 --- /dev/null +++ b/plugins/eventLogger.js @@ -0,0 +1,20 @@ +const log = require('../core/log'); +const _ = require('lodash'); +const subscriptions = require('../subscriptions'); +const config = require('../core/util').getConfig().eventLogger; + +const EventLogger = function() {} + +_.each(subscriptions, sub => { + if(config.whitelist && !config.whitelist.includes(sub.event)) { + return; + } + + EventLogger.prototype[sub.handler] = (event, next) => { + log.info(`\t\t\t\t[EVENT ${sub.event}]\n`, event); + if(_.isFunction(next)) + next(); + } +}); + +module.exports = EventLogger; \ No newline at end of file diff --git a/plugins/ifttt.js b/plugins/ifttt.js new file mode 100644 index 0000000..0b5b68b --- /dev/null +++ b/plugins/ifttt.js @@ -0,0 +1,88 @@ +const superagent = require('superagent'); +const _ = require('lodash'); +const log = require('../core/log.js'); +const util = require('../core/util.js'); +const config = util.getConfig(); +const iftttConfig = config.ifttt; + +const IFTTT = function(done) { + _.bindAll(this); + this.ifttt; + this.price = 'N/A'; + this.done = done; + this.setup(); +}; + +IFTTT.prototype.setup = function(done) { + var setupIFTTT = function () { + if(iftttConfig.sendMessageOnStart){ + var exchange = config.watch.exchange; + var currency = config.watch.currency; + var asset = config.watch.asset; + var body = "Gekko has started, Ive started watching " + +exchange + +" " + +currency + +" " + +asset + +" I'll let you know when I got some advice"; + this.send(body); + }else{ + log.debug('Skipping Send message on startup') + } + }; + setupIFTTT.call(this) +}; + +IFTTT.prototype.processCandle = function(candle, done) { + this.price = candle.close; + + done(); +}; + +IFTTT.prototype.portfolioUpdate = function(portfolio) { + var message = "Gekko has detected a portfolio update. " + + "Your current " + config.watch.currency + " balance is " + portfolio.currency + '.' + + "Your current " + config.watch.exchange + " balance is " + portfolio.assert + '.'; + this.send(message); +} + +IFTTT.prototype.processAdvice = function(advice) { + if (advice.recommendation == 'soft' && iftttConfig.muteSoft) return; + + const text = [ + 'Gekko is watching ', + config.watch.exchange, + ' and has detected a new trend, advice is to go ', + advice.recommendation, + '.\n\nThe current ', + config.watch.asset, + ' price is ', + this.price + ].join(''); + + this.send(text); +}; + +IFTTT.prototype.send = function(content) { + superagent. + post('https://maker.ifttt.com/trigger/' + iftttConfig.eventName + '/with/key/' + iftttConfig.makerKey) + .send({value1: content}) + .end(function(err, res){ + if(err || !res){ + log.error('IFTTT ERROR:', error) + }else{ + log.info('IFTTT Message Sent') + } + }); +}; + +IFTTT.prototype.checkResults = function(error) { + if (error) { + log.warn('error sending IFTTT', error); + } else { + log.info('Send advice via IFTTT.'); + } +}; + +module.exports = IFTTT; diff --git a/plugins/ircbot.js b/plugins/ircbot.js new file mode 100644 index 0000000..6cb0ff5 --- /dev/null +++ b/plugins/ircbot.js @@ -0,0 +1,154 @@ +var log = require('../core/log'); +var moment = require('moment'); +var _ = require('lodash'); +var config = require('../core/util').getConfig(); +var ircbot = config.ircbot; +var utc = moment.utc; + +var irc = require("irc"); + +var Actor = function() { + _.bindAll(this); + + this.bot = new irc.Client(ircbot.server, ircbot.botName, { + channels: [ ircbot.channel ] + }); + + this.bot.addListener("message", this.verifyQuestion); + this.bot.addListener("error", this.logError); + + this.advice = 'Dont got one yet :('; + this.adviceTime = utc(); + + this.price = 'Dont know yet :('; + this.priceTime = utc(); + + this.commands = { + ';;advice': 'emitAdvice', + ';;price': 'emitPrice', + ';;donate': 'emitDonation', + ';;real advice': 'emitRealAdvice', + ';;help': 'emitHelp' + }; + + this.rawCommands = _.keys(this.commands); +} + +Actor.prototype.processCandle = function(candle, done) { + this.price = candle.close; + this.priceTime = candle.start; + + done(); +}; + +Actor.prototype.processAdvice = function(advice) { + if (advice.recommendation == "soft" && ircbot.muteSoft) return; + this.advice = advice.recommendation; + this.adviceTime = utc(); + + if(ircbot.emitUpdates) + this.newAdvice(); +}; + +Actor.prototype.verifyQuestion = function(from, to, text, message) { + if(text in this.commands) + this[this.commands[text]](); +} + +Actor.prototype.newAdvice = function() { + this.bot.say(ircbot.channel, 'Guys! Important news!'); + this.emitAdvice(); +} + +// sent advice over to the IRC channel +Actor.prototype.emitAdvice = function() { + var message = [ + 'Advice for ', + config.watch.exchange, + ' ', + config.watch.currency, + '/', + config.watch.asset, + ' using ', + config.tradingAdvisor.method, + ' at ', + config.tradingAdvisor.candleSize, + ' minute candles, is:\n', + this.advice, + ' ', + config.watch.asset, + ' (from ', + this.adviceTime.fromNow(), + ')' + ].join(''); + + this.bot.say(ircbot.channel, message); +}; + +// sent price over to the IRC channel +Actor.prototype.emitPrice = function() { + + var message = [ + 'Current price at ', + config.watch.exchange, + ' ', + config.watch.currency, + '/', + config.watch.asset, + ' is ', + this.price, + ' ', + config.watch.currency, + ' (from ', + this.priceTime.fromNow(), + ')' + ].join(''); + + this.bot.say(ircbot.channel, message); +}; + +// sent donation info over to the IRC channel +Actor.prototype.emitDonation = function() { + var message = 'You want to donate? How nice of you! You can send your coins here:'; + message += '\nBTC:\t13r1jyivitShUiv9FJvjLH7Nh1ZZptumwW'; + + this.bot.say(ircbot.channel, message); +}; + +Actor.prototype.emitHelp = function() { + var message = _.reduce( + this.rawCommands, + function(message, command) { + return message + ' ' + command + ','; + }, + 'possible commands are:' + ); + + message = message.substr(0, _.size(message) - 1) + '.'; + + this.bot.say(ircbot.channel, message); + +} + +Actor.prototype.emitRealAdvice = function() { + // http://www.examiner.com/article/uncaged-a-look-at-the-top-10-quotes-of-gordon-gekko + // http://elitedaily.com/money/memorable-gordon-gekko-quotes/ + var realAdvice = [ + 'I don\'t throw darts at a board. I bet on sure things. Read Sun-tzu, The Art of War. Every battle is won before it is ever fought.', + 'Ever wonder why fund managers can\'t beat the S&P 500? \'Cause they\'re sheep, and sheep get slaughtered.', + 'If you\'re not inside, you\'re outside!', + 'The most valuable commodity I know of is information.', + 'It\'s not a question of enough, pal. It\'s a zero sum game, somebody wins, somebody loses. Money itself isn\'t lost or made, it\'s simply transferred from one perception to another.', + 'What\'s worth doing is worth doing for money. (Wait, wasn\'t I a free and open source bot?)', + 'When I get a hold of the son of a bitch who leaked this, I\'m gonna tear his eyeballs out and I\'m gonna suck his fucking skull.' + ]; + + this.bot.say(ircbot.channel, _.first(_.shuffle(realAdvice))); +} + +Actor.prototype.logError = function(message) { + log.error('IRC ERROR:', message); +}; + + +module.exports = Actor; diff --git a/plugins/kodi.js b/plugins/kodi.js new file mode 100644 index 0000000..9bdb035 --- /dev/null +++ b/plugins/kodi.js @@ -0,0 +1,68 @@ +/** + * Created by billymcintosh on 24/12/17. + */ + +var _ = require('lodash'); +var request = require('request'); +var log = require('../core/log.js'); +var util = require('../core/util.js'); +var config = util.getConfig(); +var kodiConfig = config.kodi; + +var Kodi = function(done) { + _.bindAll(this); + + this.exchange = config.watch.exchange.charAt().toUpperCase() + config.watch.exchange.slice(1) + + this.price = 'N/A'; + this.done = done; + this.setup(); +}; + +Kodi.prototype.setup = function(done) { + var setupKodi = function (err, result) { + if(kodiConfig.sendMessageOnStart) { + var currency = config.watch.currency; + var asset = config.watch.asset; + var title = "Gekko Started"; + var message = `Watching ${this.exchange} - ${currency}/${asset}`; + this.mail(title, message); + } else { + log.debug('Skipping Send message on startup') + } + } + setupKodi.call(this) +}; + +Kodi.prototype.processCandle = function(candle, done) { + this.price = candle.close; + + done(); +}; + +Kodi.prototype.processAdvice = function(advice) { + var title = `Gekko: Going ${advice.recommendation} @ ${this.price}` + var message = `${this.exchange} ${config.watch.currency}/${config.watch.asset}`; + this.mail(title, message); +}; + +Kodi.prototype.mail = function(title, message, done) { + var options = { + body: `{"jsonrpc":"2.0","method":"GUI.ShowNotification","params":{"title":"${title}","message":"${message}"},"id":1}`, + headers: { + 'Content-Type': 'application/json' + }, + method: 'POST', + url: kodiConfig.host + } + + request(options, (error, response, body) => { + if (!error) { + log.info('Kodi message sent') + } else { + log.debug(`Kodi ${error}`) + } + }) +} + +module.exports = Kodi; diff --git a/plugins/mailer.js b/plugins/mailer.js new file mode 100644 index 0000000..8fb8467 --- /dev/null +++ b/plugins/mailer.js @@ -0,0 +1,138 @@ +var email = require("emailjs"); +var _ = require('lodash'); +var log = require('../core/log.js'); +var util = require('../core/util.js'); +var config = util.getConfig(); +var mailConfig = config.mailer; + +var Mailer = function(done) { + _.bindAll(this); + + this.server; + this.price = 'N/A'; + + this.done = done; + this.setup(); +}; + +Mailer.prototype.setup = function(done) { + var setupMail = function(err, result) { + if(result) { + console.log('Got it.'); + mailConfig.password = result.password; + } + + if(_.isEmpty(mailConfig.to)) + mailConfig.to = mailConfig.email; + if(_.isEmpty(mailConfig.from)) + mailConfig.from = mailConfig.email; + if(_.isEmpty(mailConfig.user) && mailConfig.smtpauth) + mailConfig.user = mailConfig.email; + + this.server = email.server.connect({ + user: mailConfig.user, + password: mailConfig.password, + host: mailConfig.server, + ssl: mailConfig.ssl, + port: mailConfig.port, + tls: mailConfig.tls + }); + + if(mailConfig.sendMailOnStart) { + this.mail( + "Gekko has started", + [ + "I've just started watching ", + config.watch.exchange, + ' ', + config.watch.currency, + '/', + config.watch.asset, + ". I'll let you know when I got some advice" + ].join(''), + _.bind(function(err) { + this.checkResults(err); + this.done(); + }, this) + ); + } else + this.done(); + + log.debug('Setup email adviser.'); + }; + + if(!mailConfig.password) { + // ask for the mail password + var prompt = require('prompt-lite'); + prompt.start(); + var warning = [ + '\n\n\tYou configured Gekko to mail you advice, Gekko needs your email', + 'password to send emails (to you). Gekko is an opensource project', + '[ http://github.com/askmike/gekko ], you can take my word but always', + 'check the code yourself.', + '\n\n\tWARNING: If you have not downloaded Gekko from the github page above we', + 'CANNOT guarantuee that your email address & password are safe!\n' + ].join('\n\t'); + log.warn(warning); + prompt.get({name: 'password', hidden: true}, _.bind(setupMail, this)); + } else { + setupMail.call(this); + } +}; + +Mailer.prototype.mail = function(subject, content, done) { + + this.server.send({ + text: content, + from: mailConfig.from, + to: mailConfig.to, + subject: mailConfig.tag + subject + }, done || this.checkResults); +}; + +Mailer.prototype.processCandle = function(candle, done) { + this.price = candle.close; + + done(); +}; + +Mailer.prototype.processAdvice = function(advice) { + + if (advice.recommendation == "soft" && mailConfig.muteSoft) return; + + var text = [ + 'Gekko is watching ', + config.watch.exchange, + ' and has detected a new trend, advice is to go ', + advice.recommendation, + '.\n\nThe current ', + config.watch.asset, + ' price is ', + config.watch.currency, + ' ', + this.price + ].join(''); + + var subject = 'New advice: go ' + advice.recommendation; + + this.mail(subject, text); +}; + +Mailer.prototype.processStratNotification = function({ content }) { + const subject = `New notification from ${config.tradingAdvisor.method}`; + const text = [ + 'Gekko received new notification :\n\n', + content + ].join(''); + + this.mail(subject, text); +} + +Mailer.prototype.checkResults = function(err) { + if(err) + log.warn('error sending email', err); + else + log.info('Send advice via email.'); +}; + +module.exports = Mailer; diff --git a/plugins/mongodb/handle.js b/plugins/mongodb/handle.js new file mode 100644 index 0000000..e553d3a --- /dev/null +++ b/plugins/mongodb/handle.js @@ -0,0 +1,47 @@ +var mongojs = require('mongojs'); +var mongoUtil = require('./util'); + +var util = require('../../core/util.js'); +var config = util.getConfig(); +var dirs = util.dirs(); + +// verify the correct dependencies are installed +var pluginHelper = require(`${dirs.core}pluginUtil`); +var pluginMock = { + slug: 'mongodb adapter', + dependencies: config.mongodb.dependencies +} + +// exit if plugin couldn't be loaded +var cannotLoad = pluginHelper.cannotLoad(pluginMock); +if (cannotLoad) { + util.die(cannotLoad); +} + +var mode = util.gekkoMode(); + +var collections = [ + mongoUtil.settings.historyCollection, + mongoUtil.settings.adviceCollection +] + +var connection = mongojs(config.mongodb.connectionString, collections); +var collection = connection.collection(mongoUtil.settings.historyCollection); + +if (mode === 'backtest') { + var pair = mongoUtil.settings.pair.join('_'); + + collection.find({ pair }).toArray((err, docs) => { // check if we've got any records + if (err) { + util.die(err); + } + if (docs.length === 0) { + util.die(`History table for ${config.watch.exchange} with pair ${pair} is empty.`); + } + }) +} + +if(mongoUtil.settings.exchange) { + collection.createIndex({start: 1, pair: 1}, {unique: true}); // create unique index on "time" and "pair" +} +module.exports = connection; diff --git a/plugins/mongodb/reader.js b/plugins/mongodb/reader.js new file mode 100644 index 0000000..d4c0993 --- /dev/null +++ b/plugins/mongodb/reader.js @@ -0,0 +1,120 @@ +var _ = require('lodash'); +var util = require('../../core/util.js'); +var log = require(`${util.dirs().core}log`); + +var handle = require('./handle'); +var mongoUtil = require('./util'); + +var Reader = function Reader () { + _.bindAll(this); + this.db = handle; + this.collection = this.db.collection(mongoUtil.settings.historyCollection); + this.pair = mongoUtil.settings.pair.join('_'); +} + +// returns the furtherst point (up to `from`) in time we have valid data from +Reader.prototype.mostRecentWindow = function mostRecentWindow (from, to, next) { + var mFrom = from.unix(); + var mTo = to.unix(); + + var maxAmount = mTo - mFrom + 1; + + // Find some documents + this.collection.find({ pair: this.pair, start: { $gte: mFrom, $lte: mTo } }).sort({ start: -1 }, (err, docs) => { + if (err) { + return util.die('DB error at `mostRecentWindow`'); + } + // no candles are available + if (docs.length === 0) { + log.debug('no candles are available'); + return next(false); + } + + if (docs.length === maxAmount) { + // full history is available! + log.debug('full history is available!'); + return next({ + mFrom, + mTo + }); + } + + // we have at least one gap, figure out where + var mostRecent = _.first(docs).start; + + var gapIndex = _.findIndex(docs, (r, i) => r.start !== mostRecent - i * 60); + + // if there was no gap in the records, but + // there were not enough records. + if (gapIndex === -1) { + var leastRecent = _.last(docs).start; + return next({ + from: leastRecent, + to: mostRecent + }); + } + + // else return mostRecent and the + // the minute before the gap + return next({ + from: docs[gapIndex - 1].start, + to: mostRecent + }); + }) +} + +Reader.prototype.get = function get (from, to, what, next) { // returns all fields in general + // Find some documents + this.collection.find({ pair: this.pair, start: { $gte: from, $lte: to } }).sort({ start: 1 }, (err, docs) => { + if (err) { + return util.die('DB error at `get`'); + } + return next(null, docs); + }); +} + +Reader.prototype.count = function fCount (from, to, next) { + this.collection.count({ pair: this.pair, start: { $gte: from, $lte: to } }, (err, count) => { + if (err) { + return util.die('DB error at `count`'); + } + return next(null, count); + }) +} + +Reader.prototype.countTotal = function countTotal (next) { + this.collection.count({ pair: this.pair }, (err, count) => { + if (err) { + return util.die('DB error at `countTotal`'); + } + return next(null, count); + }) +} + +Reader.prototype.getBoundry = function getBoundry (next) { + this.collection.find({ pair: this.pair }, { start: 1 }).sort({ start: 1 }).limit(1, (err, docs) => { + if (err) { + return util.die('DB error at `getBoundry`'); + } + var start = _.first(docs).start; + + this.collection.find({ pair: this.pair }, { start: 1 }).sort({ start: -1 }).limit(1, (err2, docs2) => { + if (err2) { + return util.die('DB error at `getBoundry`'); + } + var end = _.first(docs2).start; + return next(null, { first: start, last: end }); + }); + return null; + }); +} + +Reader.prototype.tableExists = function(name, next) { + return next(null, true); // Return true for backtest +} + +Reader.prototype.close = function close () { + this.db = null; +} + +module.exports = Reader; diff --git a/plugins/mongodb/scanner.js b/plugins/mongodb/scanner.js new file mode 100644 index 0000000..5a0120b --- /dev/null +++ b/plugins/mongodb/scanner.js @@ -0,0 +1,40 @@ +const async = require('async'); +var _ = require('lodash'); +var util = require('../../core/util.js'); +var log = require(`${util.dirs().core}log`); + +var handle = require('./handle'); + +module.exports = done => { + this.db = handle; + + let markets = []; + async.waterfall([ + (cb) => { + handle.getCollectionNames(cb) + }, + (collections, cb) => { + async.each(collections, (collection, cb) => { + let [exchange, type] = collection.split('_'); + if (type === 'candles') { + handle.collection(collection).distinct('pair', {}, (err, pairs) => { + console.log(exchange); + pairs.forEach((pair) => { + pair = pair.split('_'); + markets.push({ + exchange: exchange, + currency: _.first(pair), + asset: _.last(pair) + }); + }); + cb(); + }) + } else { + cb(); + } + }, () => { + cb(null, markets) + }) + } + ], done) +} \ No newline at end of file diff --git a/plugins/mongodb/util.js b/plugins/mongodb/util.js new file mode 100644 index 0000000..feac04b --- /dev/null +++ b/plugins/mongodb/util.js @@ -0,0 +1,15 @@ +var config = require('../../core/util.js').getConfig(); + +var watch = config.watch; +var exchangeLowerCase = watch ? watch.exchange.toLowerCase() : watch = {}; // Do not crash on this, not needed to read from db + +var settings = { + exchange: watch.exchange, + pair: [watch.currency, watch.asset], + historyCollection: `${exchangeLowerCase}_candles`, + adviceCollection: `${exchangeLowerCase}_advices` +}; + +module.exports = { + settings +}; diff --git a/plugins/mongodb/writer.js b/plugins/mongodb/writer.js new file mode 100644 index 0000000..0d369e0 --- /dev/null +++ b/plugins/mongodb/writer.js @@ -0,0 +1,115 @@ +var _ = require('lodash'); +var config = require('../../core/util.js').getConfig(); + +var moment = require('moment'); +var util = require('../../core/util.js'); +var log = require(`${util.dirs().core}log`) + +var handle = require('./handle'); +var mongoUtil = require('./util'); + +var Store = function Store (done) { + _.bindAll(this); + this.done = done; + this.db = handle; + this.historyCollection = this.db.collection(mongoUtil.settings.historyCollection); + this.adviceCollection = this.db.collection(mongoUtil.settings.adviceCollection); + + this.candleCache = []; + + this.pair = mongoUtil.settings.pair.join('_'); + + this.price = 'N/A'; + this.marketTime = 'N/A'; + + done(); +} + +Store.prototype.writeCandles = function writeCandles () { + if (_.isEmpty(this.candleCache)) { // nothing to do + return; + } + + var candles = []; + _.each(this.candleCache, candle => { + var mCandle = { + time: moment().unix(), + start: candle.start.unix(), + open: candle.open, + high: candle.high, + low: candle.low, + close: candle.close, + vwp: candle.vwp, + volume: candle.volume, + trades: candle.trades, + pair: this.pair + }; + candles.push(mCandle); + }); + + // Fix error when whole batch would failed to insert if one or more duplicate candle + this.historyCollection.insert(candles, { ordered: false }, e => { + if (e) { + let msg = 'mongojs insert() ' + e.writeErrors.length + ' of ' + candles.length + ' failed.'; + _.forEach(_.countBy(e.writeErrors, 'code'), (c, k) => { + msg += ' Code: E' + k + ' count: ' + c; + }); + log.debug(msg); + } + }); + + this.candleCache = []; +} + +var processCandle = function processCandle (candle, done) { + // because we might get a lot of candles + // in the same tick, we rather batch them + // up and insert them at once at next tick. + this.price = candle.close; // used in adviceWriter + this.marketTime = candle.start; + + this.candleCache.push(candle); + if (this.candleCache.length >= 100) + this.writeCandles(); + done(); +} + +var finalize = function(done) { + this.writeCandles(); + // Fix connection closed before all candles was written to db + setTimeout( () => { + this.db = null; + done(); + }, 1000); +} + +var processAdvice = function processAdvice (advice) { + if (config.candleWriter.muteSoft && advice.recommendation === 'soft') { + return; + } + + log.debug(`Writing advice '${advice.recommendation}' to database.`); + var mAdvice = { + time: moment().unix(), + marketTime: this.marketTime, + pair: this.pair, + recommendation: advice.recommendation, + price: this.price, + portfolio: advice.portfolio + }; + + this.adviceCollection.insert(mAdvice); +} + +if (config.adviceWriter.enabled) { + log.debug('Enabling adviceWriter.'); + Store.prototype.processAdvice = processAdvice; +} + +if (config.candleWriter.enabled) { + log.debug('Enabling candleWriter.'); + Store.prototype.processCandle = processCandle; + Store.prototype.finalize = finalize; +} + +module.exports = Store; diff --git a/plugins/paperTrader/paperTrader.js b/plugins/paperTrader/paperTrader.js new file mode 100644 index 0000000..28b17bf --- /dev/null +++ b/plugins/paperTrader/paperTrader.js @@ -0,0 +1,283 @@ +const _ = require('lodash'); + +const util = require('../../core/util'); +const ENV = util.gekkoEnv(); + +const config = util.getConfig(); +const calcConfig = config.paperTrader; +const watchConfig = config.watch; +const dirs = util.dirs(); +const log = require(dirs.core + 'log'); + +const TrailingStop = require(dirs.broker + 'triggers/trailingStop'); + +const PaperTrader = function() { + _.bindAll(this); + + if(calcConfig.feeUsing === 'maker') { + this.rawFee = calcConfig.feeMaker; + } else { + this.rawFee = calcConfig.feeTaker; + } + + this.fee = 1 - this.rawFee / 100; + + this.currency = watchConfig.currency; + this.asset = watchConfig.asset; + + this.portfolio = { + asset: calcConfig.simulationBalance.asset, + currency: calcConfig.simulationBalance.currency, + } + + this.balance = false; + + if(this.portfolio.asset > 0) { + this.exposed = true; + } + + this.propogatedTrades = 0; + this.propogatedTriggers = 0; + + this.warmupCompleted = false; + + this.warmupCandle; +} + +PaperTrader.prototype.relayPortfolioChange = function() { + this.deferredEmit('portfolioChange', { + asset: this.portfolio.asset, + currency: this.portfolio.currency + }); +} + +PaperTrader.prototype.relayPortfolioValueChange = function() { + this.deferredEmit('portfolioValueChange', { + balance: this.getBalance() + }); +} + +PaperTrader.prototype.extractFee = function(amount) { + amount *= 1e8; + amount *= this.fee; + amount = Math.floor(amount); + amount /= 1e8; + return amount; +} + +PaperTrader.prototype.setStartBalance = function() { + this.balance = this.getBalance(); +} + +// after every succesfull trend ride we hopefully end up +// with more BTC than we started with, this function +// calculates Gekko's profit in %. +PaperTrader.prototype.updatePosition = function(what) { + + let cost; + let amount; + + // virtually trade all {currency} to {asset} + // at the current price (minus fees) + if(what === 'long') { + cost = (1 - this.fee) * this.portfolio.currency; + this.portfolio.asset += this.extractFee(this.portfolio.currency / this.price); + amount = this.portfolio.asset; + this.portfolio.currency = 0; + + this.exposed = true; + this.trades++; + } + + // virtually trade all {currency} to {asset} + // at the current price (minus fees) + else if(what === 'short') { + cost = (1 - this.fee) * (this.portfolio.asset * this.price); + this.portfolio.currency += this.extractFee(this.portfolio.asset * this.price); + amount = this.portfolio.currency / this.price; + this.portfolio.asset = 0; + + this.exposed = false; + this.trades++; + } + + const effectivePrice = this.price * this.fee; + + return { cost, amount, effectivePrice }; +} + +PaperTrader.prototype.getBalance = function() { + return this.portfolio.currency + this.price * this.portfolio.asset; +} + +PaperTrader.prototype.now = function() { + return this.candle.start.clone().add(1, 'minute'); +} + +PaperTrader.prototype.processAdvice = function(advice) { + let action; + if(advice.recommendation === 'short') { + action = 'sell'; + + // clean up potential old stop trigger + if(this.activeStopTrigger) { + this.deferredEmit('triggerAborted', { + id: this.activeStopTrigger.id, + date: advice.date + }); + + delete this.activeStopTrigger; + } + + } else if(advice.recommendation === 'long') { + action = 'buy'; + + if(advice.trigger) { + + // clean up potential old stop trigger + if(this.activeStopTrigger) { + this.deferredEmit('triggerAborted', { + id: this.activeStopTrigger.id, + date: advice.date + }); + + delete this.activeStopTrigger; + } + + this.createTrigger(advice); + } + } else { + return log.warn( + `[Papertrader] ignoring unknown advice recommendation: ${advice.recommendation}` + ); + } + + this.tradeId = 'trade-' + (++this.propogatedTrades); + + this.deferredEmit('tradeInitiated', { + id: this.tradeId, + adviceId: advice.id, + action, + portfolio: _.clone(this.portfolio), + balance: this.getBalance(), + date: advice.date, + }); + + const { cost, amount, effectivePrice } = this.updatePosition(advice.recommendation); + + this.relayPortfolioChange(); + this.relayPortfolioValueChange(); + + this.deferredEmit('tradeCompleted', { + id: this.tradeId, + adviceId: advice.id, + action, + cost, + amount, + price: this.price, + portfolio: this.portfolio, + balance: this.getBalance(), + date: advice.date, + effectivePrice, + feePercent: this.rawFee + }); +} + +PaperTrader.prototype.createTrigger = function(advice) { + const trigger = advice.trigger; + + if(trigger && trigger.type === 'trailingStop') { + + if(!trigger.trailValue) { + return log.warn(`[Papertrader] ignoring trailing stop without trail value`); + } + + const triggerId = 'trigger-' + (++this.propogatedTriggers); + + this.deferredEmit('triggerCreated', { + id: triggerId, + at: advice.date, + type: 'trailingStop', + proprties: { + trail: trigger.trailValue, + initialPrice: this.price, + } + }); + + this.activeStopTrigger = { + id: triggerId, + adviceId: advice.id, + instance: new TrailingStop({ + initialPrice: this.price, + trail: trigger.trailValue, + onTrigger: this.onStopTrigger + }) + } + } else { + log.warn(`[Papertrader] Gekko does not know trigger with type "${trigger.type}".. Ignoring stop.`); + } +} + +PaperTrader.prototype.onStopTrigger = function() { + + const date = this.now(); + + this.deferredEmit('triggerFired', { + id: this.activeStopTrigger.id, + date + }); + + const { cost, amount, effectivePrice } = this.updatePosition('short'); + + this.relayPortfolioChange(); + this.relayPortfolioValueChange(); + + this.deferredEmit('tradeCompleted', { + id: this.tradeId, + adviceId: this.activeStopTrigger.adviceId, + action: 'sell', + cost, + amount, + price: this.price, + portfolio: this.portfolio, + balance: this.getBalance(), + date, + effectivePrice, + feePercent: this.rawFee + }); + + delete this.activeStopTrigger; +} + +PaperTrader.prototype.processStratWarmupCompleted = function() { + this.warmupCompleted = true; + this.processCandle(this.warmupCandle, _.noop); +} + +PaperTrader.prototype.processCandle = function(candle, done) { + if(!this.warmupCompleted) { + this.warmupCandle = candle; + return done(); + } + + this.price = candle.close; + this.candle = candle; + + if(!this.balance) { + this.setStartBalance(); + this.relayPortfolioChange(); + this.relayPortfolioValueChange(); + } + + if(this.exposed) { + this.relayPortfolioValueChange(); + } + + if(this.activeStopTrigger) { + this.activeStopTrigger.instance.updatePrice(this.price); + } + + done(); +} + +module.exports = PaperTrader; diff --git a/plugins/performanceAnalyzer/logger.js b/plugins/performanceAnalyzer/logger.js new file mode 100644 index 0000000..55d3fb0 --- /dev/null +++ b/plugins/performanceAnalyzer/logger.js @@ -0,0 +1,134 @@ +// log trade performance results + +const _ = require('lodash'); +const moment = require('moment'); +const humanizeDuration = require('humanize-duration'); + +const util = require('../../core/util.js'); +const dirs = util.dirs(); +const mode = util.gekkoMode(); +const log = require(dirs.core + 'log'); + +const Logger = function(watchConfig) { + this.currency = watchConfig.currency; + this.asset = watchConfig.asset; + + this.roundtrips = []; +} + +Logger.prototype.round = function(amount) { + return amount.toFixed(8); +} + +// used for: +// - realtime logging (per advice) +// - backtest logging (on finalize) +Logger.prototype.logReport = function(trade, report) { + // ignore the trade + + var start = this.round(report.startBalance); + var current = this.round(report.balance); + + log.info(`(PROFIT REPORT) original balance:\t\t ${start} ${this.currency}`); + log.info(`(PROFIT REPORT) current balance:\t\t ${current} ${this.currency}`); + log.info( + `(PROFIT REPORT) profit:\t\t\t\t ${this.round(report.profit)} ${this.currency}`, + `(${this.round(report.relativeProfit)}%)` + ); +} + +Logger.prototype.logRoundtripHeading = function() { + log.info('(ROUNDTRIP)', 'entry date (UTC) \texit date (UTC) \texposed duration\tP&L \tprofit'); +} + +Logger.prototype.logRoundtrip = function(rt) { + const display = [ + rt.entryAt.utc().format('YYYY-MM-DD HH:mm'), + rt.exitAt.utc().format('YYYY-MM-DD HH:mm'), + (moment.duration(rt.duration).humanize() + " ").slice(0, 16), + rt.pnl.toFixed(2), + rt.profit.toFixed(2) + ]; + + log.info('(ROUNDTRIP)', display.join('\t')); +} + +if(mode === 'backtest') { + // we only want to log a summarized one line report, like: + // 2016-12-19 20:12:00: Paper trader simulated a BUY 0.000 USDT => 1.098 BTC + Logger.prototype.handleTrade = function(trade) { + if(trade.action !== 'sell' && trade.action !== 'buy') + return; + + var at = trade.date.format('YYYY-MM-DD HH:mm:ss'); + + + if(trade.action === 'sell') + + log.info( + `${at}: Paper trader simulated a SELL`, + `\t${this.round(trade.portfolio.currency)}`, + `${this.currency} <= ${this.round(trade.portfolio.asset)}`, + `${this.asset}` + ); + + else if(trade.action === 'buy') + + log.info( + `${at}: Paper trader simulated a BUY`, + `\t${this.round(trade.portfolio.currency)}`, + `${this.currency}\t=> ${this.round(trade.portfolio.asset)}`, + `${this.asset}` + ); + } + + Logger.prototype.finalize = function(report) { + + log.info(); + log.info('(ROUNDTRIP) REPORT:'); + + this.logRoundtripHeading(); + _.each(this.roundtrips, this.logRoundtrip, this); + + log.info() + log.info(`(PROFIT REPORT) start time:\t\t\t ${report.startTime}`); + log.info(`(PROFIT REPORT) end time:\t\t\t ${report.endTime}`); + log.info(`(PROFIT REPORT) timespan:\t\t\t ${report.timespan}`); + log.info(`(PROFIT REPORT) exposure:\t\t\t ${report.exposure}`); + log.info(); + log.info(`(PROFIT REPORT) start price:\t\t\t ${report.startPrice} ${this.currency}`); + log.info(`(PROFIT REPORT) end price:\t\t\t ${report.endPrice} ${this.currency}`); + log.info(`(PROFIT REPORT) Market:\t\t\t\t ${this.round(report.market)}%`); + log.info(); + log.info(`(PROFIT REPORT) amount of trades:\t\t ${report.trades}`); + + this.logReport(null, report); + + log.info( + `(PROFIT REPORT) simulated yearly profit:\t ${report.yearlyProfit}`, + `${this.currency} (${report.relativeYearlyProfit}%)` + ); + + log.info(`(PROFIT REPORT) sharpe ratio:\t\t\t ${report.sharpe}`); + log.info(`(PROFIT REPORT) expected downside:\t\t ${report.downside}`); + log.info(`(PROFIT REPORT) ratio roundtrips:\t\t ${report.ratioRoundTrips}%`); + } + + Logger.prototype.handleRoundtrip = function(rt) { + this.roundtrips.push(rt); + } + +} else if(mode === 'realtime') { + Logger.prototype.handleTrade = Logger.prototype.logReport; + + Logger.prototype.handleRoundtrip = function(rt) { + this.logRoundtripHeading(); + this.logRoundtrip(rt); + } + +} + + + + +module.exports = Logger; diff --git a/plugins/performanceAnalyzer/performanceAnalyzer.js b/plugins/performanceAnalyzer/performanceAnalyzer.js new file mode 100644 index 0000000..ee9fe74 --- /dev/null +++ b/plugins/performanceAnalyzer/performanceAnalyzer.js @@ -0,0 +1,251 @@ + +const _ = require('lodash'); +const moment = require('moment'); + +const statslite = require('stats-lite'); +const util = require('../../core/util'); +const log = require(util.dirs().core + 'log') +const ENV = util.gekkoEnv(); + +const config = util.getConfig(); +const perfConfig = config.performanceAnalyzer; +const watchConfig = config.watch; + +const Logger = require('./logger'); + +const PerformanceAnalyzer = function() { + _.bindAll(this); + + this.dates = { + start: false, + end: false + } + + this.startPrice = 0; + this.endPrice = 0; + + this.currency = watchConfig.currency; + this.asset = watchConfig.asset; + + this.logger = new Logger(watchConfig); + + this.trades = 0; + + this.exposure = 0; + + this.roundTrips = []; + this.losses = []; + this.roundTrip = { + id: 0, + entry: false, + exit: false + } + + this.portfolio = {}; + this.balance; + + this.start = {}; + this.openRoundTrip = false; + + this.warmupCompleted = false; +} + +PerformanceAnalyzer.prototype.processPortfolioValueChange = function(event) { + if(!this.start.balance) { + this.start.balance = event.balance; + } + + this.balance = event.balance; +} + +PerformanceAnalyzer.prototype.processPortfolioChange = function(event) { + if(!this.start.portfolio) { + this.start.portfolio = event; + } +} + +PerformanceAnalyzer.prototype.processStratWarmupCompleted = function() { + this.warmupCompleted = true; + this.processCandle(this.warmupCandle, _.noop); +} + +PerformanceAnalyzer.prototype.processCandle = function(candle, done) { + if(!this.warmupCompleted) { + this.warmupCandle = candle; + return done(); + } + + this.price = candle.close; + this.dates.end = candle.start.clone().add(1, 'minute'); + + if(!this.dates.start) { + this.dates.start = candle.start; + this.startPrice = candle.close; + } + + this.endPrice = candle.close; + + if(this.openRoundTrip) { + this.emitRoundtripUpdate(); + } + + done(); +} + +PerformanceAnalyzer.prototype.emitRoundtripUpdate = function() { + const uPnl = this.price - this.roundTrip.entry.price; + + this.deferredEmit('roundtripUpdate', { + at: this.dates.end, + duration: this.dates.end.diff(this.roundTrip.entry.date), + uPnl, + uProfit: uPnl / this.roundTrip.entry.total * 100 + }) +} + +PerformanceAnalyzer.prototype.processTradeCompleted = function(trade) { + this.trades++; + this.portfolio = trade.portfolio; + this.balance = trade.balance; + + this.registerRoundtripPart(trade); + + const report = this.calculateReportStatistics(); + if(report) { + this.logger.handleTrade(trade, report); + this.deferredEmit('performanceReport', report); + } +} + +PerformanceAnalyzer.prototype.registerRoundtripPart = function(trade) { + if(this.trades === 1 && trade.action === 'sell') { + // this is not part of a valid roundtrip + return; + } + + if(trade.action === 'buy') { + if (this.roundTrip.exit) { + this.roundTrip.id++; + this.roundTrip.exit = false + } + + this.roundTrip.entry = { + date: trade.date, + price: trade.price, + total: trade.portfolio.currency + (trade.portfolio.asset * trade.price), + } + this.openRoundTrip = true; + } else if(trade.action === 'sell') { + this.roundTrip.exit = { + date: trade.date, + price: trade.price, + total: trade.portfolio.currency + (trade.portfolio.asset * trade.price), + } + this.openRoundTrip = false; + + this.handleCompletedRoundtrip(); + } +} + +PerformanceAnalyzer.prototype.handleCompletedRoundtrip = function() { + var roundtrip = { + id: this.roundTrip.id, + + entryAt: this.roundTrip.entry.date, + entryPrice: this.roundTrip.entry.price, + entryBalance: this.roundTrip.entry.total, + + exitAt: this.roundTrip.exit.date, + exitPrice: this.roundTrip.exit.price, + exitBalance: this.roundTrip.exit.total, + + duration: this.roundTrip.exit.date.diff(this.roundTrip.entry.date) + } + + roundtrip.pnl = roundtrip.exitBalance - roundtrip.entryBalance; + roundtrip.profit = (100 * roundtrip.exitBalance / roundtrip.entryBalance) - 100; + + this.roundTrips[this.roundTrip.id] = roundtrip; + + this.logger.handleRoundtrip(roundtrip); + + this.deferredEmit('roundtrip', roundtrip); + + // update cached exposure + this.exposure = this.exposure + Date.parse(this.roundTrip.exit.date) - Date.parse(this.roundTrip.entry.date); + // track losses separately for downside report + if (roundtrip.exitBalance < roundtrip.entryBalance) + this.losses.push(roundtrip); + +} + +PerformanceAnalyzer.prototype.calculateReportStatistics = function() { + if(!this.start.balance || !this.start.portfolio) { + log.error('Cannot calculate a profit report without having received portfolio data.'); + log.error('Skipping performanceReport..'); + return false; + } + + // the portfolio's balance is measured in {currency} + const profit = this.balance - this.start.balance; + + const timespan = moment.duration( + this.dates.end.diff(this.dates.start) + ); + const relativeProfit = this.balance / this.start.balance * 100 - 100; + const relativeYearlyProfit = relativeProfit / timespan.asYears(); + + const percentExposure = this.exposure / (Date.parse(this.dates.end) - Date.parse(this.dates.start)); + + const sharpe = (relativeYearlyProfit - perfConfig.riskFreeReturn) + / statslite.stdev(this.roundTrips.map(r => r.profit)) + / Math.sqrt(this.trades / (this.trades - 2)); + + const downside = statslite.percentile(this.losses.map(r => r.profit), 0.25) + * Math.sqrt(this.trades / (this.trades - 2)); + + const ratioRoundTrips = this.roundTrips.length > 0 ? (this.roundTrips.filter(roundTrip => roundTrip.pnl > 0 ).length / this.roundTrips.length * 100).toFixed(4) : 100; + + const report = { + startTime: this.dates.start.utc().format('YYYY-MM-DD HH:mm:ss'), + endTime: this.dates.end.utc().format('YYYY-MM-DD HH:mm:ss'), + timespan: timespan.humanize(), + market: this.endPrice * 100 / this.startPrice - 100, + + balance: this.balance, + profit, + relativeProfit: relativeProfit, + + yearlyProfit: profit / timespan.asYears(), + relativeYearlyProfit, + + startPrice: this.startPrice, + endPrice: this.endPrice, + trades: this.trades, + startBalance: this.start.balance, + exposure: percentExposure, + sharpe, + downside, + ratioRoundTrips + } + + report.alpha = report.relativeProfit - report.market; + + return report; +} + +PerformanceAnalyzer.prototype.finalize = function(done) { + if(!this.trades) { + return done(); + } + + const report = this.calculateReportStatistics(); + if(report) { + this.logger.finalize(report); + this.emit('performanceReport', report); + } + done(); +} + + +module.exports = PerformanceAnalyzer; diff --git a/plugins/postgresql/handle.js b/plugins/postgresql/handle.js new file mode 100755 index 0000000..0bf52a6 --- /dev/null +++ b/plugins/postgresql/handle.js @@ -0,0 +1,114 @@ +const _ = require('lodash'); +const fs = require('fs'); + +const util = require('../../core/util.js'); +const config = util.getConfig(); +const dirs = util.dirs(); + +const log = require(util.dirs().core + 'log'); +const postgresUtil = require('./util'); + +const adapter = config.postgresql; + +// verify the correct dependencies are installed +const pluginHelper = require(dirs.core + 'pluginUtil'); +const pluginMock = { + slug: 'postgresql adapter', + dependencies: config.postgresql.dependencies +} + +const cannotLoad = pluginHelper.cannotLoad(pluginMock); +if(cannotLoad) { + util.die(cannotLoad); +} + +const pg = require('pg'); + +const version = adapter.version; + +const dbName = postgresUtil.database(); + +const mode = util.gekkoMode(); + +const connectionString = config.postgresql.connectionString; + +const checkClient = new pg.Pool({ + connectionString: connectionString + '/postgres', +}); +const pool = new pg.Pool({ + connectionString: connectionString + '/' + dbName, +}); + +// We need to check if the db exists first. +// This requires connecting to the default +// postgres database first. Your postgres +// user will need appropriate rights. +checkClient.connect((err, client, done) => { + if(err) { + util.die(err); + } + + log.debug("Check database exists: " + dbName); + client.query("select count(*) from pg_catalog.pg_database where datname = $1", [dbName], + (err, res) => { + if(err) { + util.die(err); + } + + if(res.rows[0].count !== '0') { + // database exists + log.debug("Database exists: " + dbName); + log.debug("Postgres connection pool is ready, db " + dbName); + upsertTables(); + done(); + return; + } + + // database dot NOT exist + + if(mode === 'backtest') { + // no point in trying to backtest with + // non existing data. + util.die(`History does not exist for exchange ${config.watch.exchange}.`); + } + + createDatabase(client, done); + }); +}); + +const createDatabase = (client, done) => { + client.query("CREATE DATABASE " + dbName, err => { + if(err) { + util.die(err); + } + + log.debug("Postgres connection pool is ready, db " + dbName); + done(); + upsertTables(); + }); +} + +const upsertTables = () => { + const upsertQuery = + `CREATE TABLE IF NOT EXISTS + ${postgresUtil.table('candles')} ( + id BIGSERIAL PRIMARY KEY, + start integer UNIQUE, + open double precision NOT NULL, + high double precision NOT NULL, + low double precision NOT NULL, + close double precision NOT NULL, + vwp double precision NOT NULL, + volume double precision NOT NULL, + trades INTEGER NOT NULL + );`; + + pool.query(upsertQuery, (err) => { + if(err) { + util.die(err); + } + }); +} + + +module.exports = pool; diff --git a/plugins/postgresql/reader.js b/plugins/postgresql/reader.js new file mode 100755 index 0000000..32891bd --- /dev/null +++ b/plugins/postgresql/reader.js @@ -0,0 +1,209 @@ +var _ = require('lodash'); +var util = require('../../core/util.js'); +var config = util.getConfig(); +var log = require(util.dirs().core + 'log'); + +var handle = require('./handle'); +var postgresUtil = require('./util'); + +const { Query } = require('pg'); + +var Reader = function() { + _.bindAll(this); + this.db = handle; +} + +// returns the furthest point (up to `from`) in time we have valid data from +Reader.prototype.mostRecentWindow = function(from, to, next) { + to = to.unix(); + from = from.unix(); + + var maxAmount = to - from + 1; + + this.db.connect((err, client, done) => { + + if(err) { + log.error(err); + return util.die(err.message); + } + + var query = client.query(new Query(` + SELECT start from ${postgresUtil.table('candles')} + WHERE start <= ${to} AND start >= ${from} + ORDER BY start DESC + `), function (err, result) { + if (err) { + // bail out if the table does not exist + if (err.message.indexOf(' does not exist') !== -1) + return next(false); + + log.error(err); + return util.die('DB error while reading mostRecentWindow'); + } + }); + + var rows = []; + query.on('row', function(row) { + rows.push(row); + }); + + // After all data is returned, close connection and return results + query.on('end', function() { + done(); + // no candles are available + if(rows.length === 0) { + return next(false); + } + + if(rows.length === maxAmount) { + + // full history is available! + + return next({ + from: from, + to: to + }); + } + + // we have at least one gap, figure out where + var mostRecent = _.first(rows).start; + + var gapIndex = _.findIndex(rows, function(r, i) { + return r.start !== mostRecent - i * 60; + }); + + // if there was no gap in the records, but + // there were not enough records. + if(gapIndex === -1) { + var leastRecent = _.last(rows).start; + return next({ + from: leastRecent, + to: mostRecent + }); + } + + // else return mostRecent and the + // the minute before the gap + return next({ + from: rows[ gapIndex - 1 ].start, + to: mostRecent + }); + }); + }); +} + +Reader.prototype.tableExists = function (name, next) { + this.db.connect((err,client,done) => { + client.query(` + SELECT table_name + FROM information_schema.tables + WHERE table_schema='${postgresUtil.schema()}' + AND table_name='${postgresUtil.table(name)}'; + `, function(err, result) { + done(); + if (err) { + return util.die('DB error at `tableExists`'); + } + + next(null, result.rows.length === 1); + }); + }); +} + +Reader.prototype.get = function(from, to, what, next) { + if(what === 'full'){ + what = '*'; + } + + this.db.connect((err,client,done) => { + var query = client.query(new Query(` + SELECT ${what} from ${postgresUtil.table('candles')} + WHERE start <= ${to} AND start >= ${from} + ORDER BY start ASC + `)); + + var rows = []; + query.on('row', function(row) { + rows.push(row); + }); + + query.on('end',function(){ + done(); + next(null, rows); + }); + }); +} + +Reader.prototype.count = function(from, to, next) { + this.db.connect((err,client,done) => { + if(err) { + log.error(err); + return util.die(err.message); + } + + var query = client.query(new Query(` + SELECT COUNT(*) as count from ${postgresUtil.table('candles')} + WHERE start <= ${to} AND start >= ${from} + `)); + var rows = []; + query.on('row', function(row) { + rows.push(row); + }); + + query.on('end',function(){ + done(); + next(null, _.first(rows).count); + }); + }); +} + +Reader.prototype.countTotal = function(next) { + this.db.connect((err,client,done) => { + var query = client.query(new Query(` + SELECT COUNT(*) as count from ${postgresUtil.table('candles')} + `)); + var rows = []; + query.on('row', function(row) { + rows.push(row); + }); + + query.on('end',function(){ + done(); + next(null, _.first(rows).count); + }); + }); +} + +Reader.prototype.getBoundry = function(next) { + this.db.connect((err,client,done) => { + var query = client.query(new Query(` + SELECT ( + SELECT start + FROM ${postgresUtil.table('candles')} + ORDER BY start LIMIT 1 + ) as first, + ( + SELECT start + FROM ${postgresUtil.table('candles')} + ORDER BY start DESC + LIMIT 1 + ) as last + `)); + var rows = []; + query.on('row', function(row) { + rows.push(row); + }); + + query.on('end',function(){ + done(); + next(null, _.first(rows)); + }); + }); +} + +Reader.prototype.close = function() { + //obsolete due to connection pooling + //this.db.end(); +} + +module.exports = Reader; diff --git a/plugins/postgresql/scanner.js b/plugins/postgresql/scanner.js new file mode 100644 index 0000000..c3e4fec --- /dev/null +++ b/plugins/postgresql/scanner.js @@ -0,0 +1,93 @@ +const _ = require('lodash'); +const async = require('async'); +var pg = require('pg'); + +const util = require('../../core/util.js'); +const config = util.getConfig(); +const dirs = util.dirs(); +var postgresUtil = require('./util'); + +var connectionString = config.postgresql.connectionString; + + +module.exports = done => { + var scanClient = new pg.Client(connectionString+"/postgres"); + + let markets = []; + + scanClient.connect(function (err) { + if(err){ + util.die(err); + } + + var sql = "select datname from pg_database"; + + // In single DB setup we don't need to go look into other DBs + if (postgresUtil.useSingleDatabase()) { + sql = "select datname from pg_database where datname='" + postgresUtil.database() + "'"; + } + + var query = scanClient.query(sql, function (err, result) { + + async.each(result.rows, (dbRow, next) => { + + var scanTablesClient = new pg.Client(connectionString + "/" + dbRow.datname); + var dbName = dbRow.datname; + + scanTablesClient.connect(function (err) { + if (err) { + return next(); + } + + var query = scanTablesClient.query(` + SELECT table_name + FROM information_schema.tables + WHERE table_schema='${postgresUtil.schema()}'; + `, function(err, result) { + if (err) { + return util.die('DB error at `scanning tables`'); + } + + _.each(result.rows, table => { + let parts = table.table_name.split('_'); + let first = parts.shift(); + let exchangeName = dbName; + + /** + * If using single database, we need to strip + * exchange from table name. See here how tables + * are named: + * + * - in single database setup: poloniex_candles_usdt_btc + * - in multi db setup: db = poloniex, table = candles_usdt_btc + */ + if (postgresUtil.useSingleDatabase()) { + exchangeName = first; + first = parts.shift(); + } + + if(first === 'candles') + markets.push({ + exchange: exchangeName, + currency: _.first(parts), + asset: _.last(parts) + }); + }); + + scanTablesClient.end(); + + next(); + }); + }); + + }, + // got all tables! + err => { + scanClient.end(); + done(err, markets); + }); + + }); + + }); +} \ No newline at end of file diff --git a/plugins/postgresql/util.js b/plugins/postgresql/util.js new file mode 100755 index 0000000..b322179 --- /dev/null +++ b/plugins/postgresql/util.js @@ -0,0 +1,66 @@ +var config = require('../../core/util.js').getConfig(); + +var watch = config.watch; +if(watch) { + var settings = { + exchange: watch.exchange, + pair: [watch.currency, watch.asset] + } +} + +/** + * Returns true if we use single database where + * all our tables are stored. The default is to store + * every exchange into it's own db. + * + * Set config.postgresql.database to use single db setup + */ +function useSingleDatabase() { + return !!config.postgresql.database; +} + +/** + * Postgres has tables in lowercase if you don't + * escape their names. Which we don't and so let's + * just lowercase them. + */ +function useLowerCaseTableNames() { + return !config.postgresql.noLowerCaseTableName; +} + +module.exports = { + settings: settings, + + // true if we have single db setup (see postrgesql.database config key) + useSingleDatabase: useSingleDatabase, + + // returns DB name (depends on single db setup) + database: function () { + return useSingleDatabase() ? + config.postgresql.database : + config.watch.exchange.toLowerCase().replace(/\-/g,''); + }, + + // returns table name which can be different if we use + // single or multiple db setup. + table: function (name) { + if (useSingleDatabase()) { + name = watch.exchange.replace(/\-/g,'') + '_' + name; + } + var fullName = [name, settings.pair.join('_')].join('_'); + return useLowerCaseTableNames() ? fullName.toLowerCase() : fullName; + }, + + startconstraint: function (name) { + if (useSingleDatabase()) { + name = watch.exchange.replace(/\-/g,'') + '_' + name; + } + var fullName = [name, settings.pair.join('_')].join('_'); + return useLowerCaseTableNames() ? fullName.toLowerCase() + '_start_key' : fullName + '_start_key'; + }, + + // postgres schema name. defaults to 'public' + schema: function () { + return config.postgresql.schema ? config.postgresql.schema : 'public'; + } +} diff --git a/plugins/postgresql/writer.js b/plugins/postgresql/writer.js new file mode 100755 index 0000000..7a96fdb --- /dev/null +++ b/plugins/postgresql/writer.js @@ -0,0 +1,73 @@ +var _ = require('lodash'); +const log = require('../../core/log'); +const util = require('../../core/util'); +const config = util.getConfig(); + +var handle = require('./handle'); +var postgresUtil = require('./util'); + +var Store = function(done, pluginMeta) { + _.bindAll(this); + this.done = done; + this.db = handle; + this.cache = []; + done(); +} + +Store.prototype.writeCandles = function() { + if(_.isEmpty(this.cache)){ + return; + } + + //log.debug('Writing candles to DB!'); + _.each(this.cache, candle => { + var stmt = ` + BEGIN; + LOCK TABLE ${postgresUtil.table('candles')} IN SHARE ROW EXCLUSIVE MODE; + INSERT INTO ${postgresUtil.table('candles')} + (start, open, high,low, close, vwp, volume, trades) + VALUES + (${candle.start.unix()}, ${candle.open}, ${candle.high}, ${candle.low}, ${candle.close}, ${candle.vwp}, ${candle.volume}, ${candle.trades}) + ON CONFLICT ON CONSTRAINT ${postgresUtil.startconstraint('candles')} + DO NOTHING; + COMMIT; + `; + + this.db.connect((err,client,done) => { + if(err) { + util.die(err); + } + client.query(stmt, (err, res) => { + done(); + if (err) { + log.debug(err.stack) + } else { + //log.debug(res) + } + }); + }); + }); + + this.cache = []; +} + +var processCandle = function(candle, done) { + this.cache.push(candle); + if (this.cache.length > 1) + this.writeCandles(); + + done(); +}; + +var finalize = function(done) { + this.writeCandles(); + this.db = null; + done(); +} + +if(config.candleWriter.enabled) { + Store.prototype.processCandle = processCandle; + Store.prototype.finalize = finalize; +} + +module.exports = Store; diff --git a/plugins/pushbullet.js b/plugins/pushbullet.js new file mode 100644 index 0000000..4efb2d2 --- /dev/null +++ b/plugins/pushbullet.js @@ -0,0 +1,311 @@ +/** + * Created by rocketman1337345 on 8/14/16. + * Extended by RJPGriffin (Gryphon) on 30/5/18 + */ + + +/** +Required Config: + +config.pushbullet = { + // sends pushbullets if true + enabled: true, + // Send 'Gekko starting' message if true + sendMessageOnStart: true, + // Send Message for advice? Recommend Flase for paper, true for live + sendOnAdvice: true, + // Send Message on Trade Completion? + sendOnTrade: true, + // For Overall P/L calc. Pass in old balance if desired, else leave '0' + startingBalance: '0', + // your pushbullet API key + key: '', + // your email + email: 'jon_snow@westeros.com', + // Messages will start with this tag + tag: '[GEKKO]' +}; + + + **/ + +var pushbullet = require("pushbullet"); +var _ = require('lodash'); +const moment = require('moment'); +const request = require('request'); +var log = require('../core/log.js'); +var util = require('../core/util.js'); +var config = util.getConfig(); +var pbConf = config.pushbullet; + +var Pushbullet = function(done) { + _.bindAll(this); + + this.pusher; + this.price = 'N/A'; + this.startingBalance = pbConf.startingBalance === undefined ? 0 : Number(pbConf.startingBalance); + this.advicePrice = 0; + this.adviceTime = moment(); + this.lastBuyTime = moment(); + this.lastBuyBalance = 0; + this.hasBought = 0; + + this.done = done; + this.setup(); +}; + +Pushbullet.prototype.setup = function(done) { + + var setupPushBullet = function(err, result) { + if (pbConf.sendMessageOnStart) { + var title = pbConf.tag; + var exchange = config.watch.exchange; + var currency = config.watch.currency; + var asset = config.watch.asset; + let tradeType = 'watching'; + if (config.trader.enabled) { + tradeType = "Live Trading"; + } + if (config.paperTrader.enabled) { + tradeType = "Paper Trading"; + } + + var body = `Gekko has started ${tradeType} ${asset}/${currency} on ${exchange}.`; + + //If trading Advisor is enabled, add strategy and candle size information + if (config.tradingAdvisor.enabled) { + body += `\n\nUsing ${config.tradingAdvisor.method} strategy on M${config.tradingAdvisor.candleSize} candles.` + } + + this.mail(title, body); + } else { + log.debug('Skipping Send message on startup') + } + }; + setupPushBullet.call(this) +}; + +Pushbullet.prototype.processCandle = function(candle, done) { + this.price = candle.close; + + done(); +}; + + +Pushbullet.prototype.processAdvice = function(advice) { + + this.advicePrice = this.price; + this.adviceTime = advice.date; + + if (pbConf.sendOnAdvice) { + + var text = [ + 'Gekko has new advice for ', + capF(config.watch.exchange), + ', advice is to go ', + capF(advice.recommendation), + '.\n\nThe current ', + config.watch.asset, + ' price is ', + this.advicePrice + ].join(''); + + var subject = pbConf.tag + ' New advice: go ' + advice.recommendation; + + this.mail(subject, text); + } +}; + +Pushbullet.prototype.processTradeCompleted = function(trade) { + + //Check Starting balance is initialized - if 0, initialize it + this.startingBalance = this.startingBalance ? this.startingBalance : trade.balance; + + // If config variable doesn't exist (old config) defaults to send + let sendOnTrade = pbConf.sendOnTrade === undefined ? 1 : pbConf.sendOnTrade; + + if (sendOnTrade) { + + // Calculate exposure Time + let exposureTimeStr = ''; + let balanceChangeStr = '\n'; + let totBalanceChangeStr = ''; + let subject = `${pbConf.tag} ${capF(trade.action)} complete`; + + if (trade.action === 'buy') { + this.hasBought = 1; // Flag to ensure that the following variables have been filled + this.lastBuyTime = trade.date; + this.lastBuyBalance = trade.balance; + } else if (this.hasBought) { //if sell and we have previous buy data + exposureTimeStr = `\nExposure Time: ${moment.duration(trade.date.diff(this.lastBuyTime)).humanize()}`; + + //Calculate balance change + let oBal = this.lastBuyBalance; // Old Balance + let nBal = trade.balance; // New Balance + let diffBal = Math.abs(nBal - oBal); // Balance Difference + let percDiffBal = (diffBal / oBal) * 100; // Percentage difference + + + if (nBal >= oBal) { // profit! + balanceChangeStr = `\n\nRound trip profit of: \n${getNumStr(diffBal)}${config.watch.currency} \n${getNumStr(percDiffBal,2)}%\n` + subject = `${subject}: +${getNumStr(percDiffBal,2)}%` + } else if (nBal < oBal) { // Loss :( + balanceChangeStr = `\n\nRound trip loss of: \n-${getNumStr(diffBal)}${config.watch.currency} \n-${getNumStr(percDiffBal,2)}%\n` + subject = `${subject}: -${getNumStr(percDiffBal,2)}%` + } + + //Calculate overall P/l + let sBal = this.startingBalance; + let tDiffBal = Math.abs(nBal - sBal); + let percDiffTotBal = (tDiffBal / sBal) * 100; + if (nBal >= sBal) { // profit! + totBalanceChangeStr = `\nOverall gain of: \n${getNumStr(tDiffBal)}${config.watch.currency} \n${getNumStr(percDiffTotBal,2)}%\n` + } else if (nBal < sBal) { // Loss :( + totBalanceChangeStr = `\nOverall loss of \n-${getNumStr(tDiffBal)}${config.watch.currency} \n-${getNumStr(percDiffTotBal,2)}%\n` + + } else if (trade.action === 'sell' && !this.hasBought) { + balanceChangeStr = `\n\nNot enough data for exposure time, round trip or overall performance yet. This will appear after bot has completed first round trip.` + } + } + + let costOfTradeStr = `\nCost of Trade: ${getNumStr(trade.cost)}${config.watch.currency}, ${getNumStr(((trade.cost / (trade.amount*trade.price)) * 100), 2)}%`; + + //build strings that are only sent for Live trading, not paperTrader + let orderFillTimeStr = ''; + let slippageStr = ''; + + if (!config.paperTrader.enabled) { + let timeToComplete = moment.duration(trade.date.diff(this.adviceTime)).humanize(); + orderFillTimeStr = `\nOrder fill Time: ${timeToComplete}`; + + var slip; + //Slip direction is opposite for buy and sell + if (trade.price === this.advicePrice) { + slip = 0; + } else if (trade.action === 'buy') { + slip = 100 * ((trade.price - this.advicePrice) / this.advicePrice); + } else if (trade.action === 'sell') { + slip = 100 * ((this.advicePrice - trade.price) / this.advicePrice); + } + slippageStr = `\nSlipped ${getNumStr(slip,2)}% from advice @ ${getNumStr(this.advicePrice)}`; + + + } + + var text = [ + capF(config.watch.exchange), ' ', config.watch.asset, '/', config.watch.currency, + `\n\n${config.watch.asset} Trade Price: ${getNumStr(trade.price)}`, + `\n${getPastTense(trade.action)} ${getNumStr(trade.amount)} ${config.watch.asset}`, + orderFillTimeStr, + slippageStr, + costOfTradeStr, + exposureTimeStr, + balanceChangeStr, + totBalanceChangeStr, + '\nBalance: ', getNumStr(trade.balance), config.watch.currency, + ].join(''); + + this.mail(subject, text); + } +}; + + +// A long winded function to make sure numbers aren't displayed with too many decimal places +// and are a little humanized +function getNumStr(num, fixed = 4) { + let numStr = ''; + + if (typeof num != "number") { + num = Number(num); + if (isNaN(num)) { + // console.log("Pushbullet Plugin: Number Conversion Failed"); + return "Conversion Failure"; + } + } + + + if (Number.isInteger(num)) { + numStr = num.toString(); + } else { + + //Create modNum Max - Must be a better way... + let modNumMax = '1'; + for (let i = 1; i < fixed; i++) { + modNumMax = modNumMax + '0'; + } + modNumMax = Number(modNumMax); + + let i = 0; + if (num < 1) { + let modNum = num - Math.floor(num); + while (modNum < modNumMax && i < 8) { + modNum *= 10; + i += 1; + } + } else { + i = fixed; + } + numStr = num.toFixed(i); + //Remove any excess zeros + while (numStr.charAt(numStr.length - 1) === '0') { + numStr = numStr.substring(0, numStr.length - 1); + } + + //If last char remaining is a decimal point, remove it + if (numStr.charAt(numStr.length - 1) === '.') { + numStr = numStr.substring(0, numStr.length - 1); + } + + } + + //Add commas for thousands etc + let dp = numStr.indexOf('.'); //find deciaml point + if (dp < 0) { //no dp found + dp = numStr.length; + } + + let insPos = dp - 3; + insCount = 0; + while (insPos > 0) { + insCount++; + numStr = numStr.slice(0, insPos) + ',' + numStr.slice(insPos); + insPos -= 3; + } + + + return (numStr); +} + +function capF(inWord) { //Capitalise first letter of string + return (inWord.charAt(0).toUpperCase() + inWord.slice(1)); +} + +function getPastTense(action) { + let ret = ''; + if (action === 'buy') { + ret = 'Bought' + } else if (action === 'sell') { + ret = 'Sold' + } + return ret; +} + +Pushbullet.prototype.mail = function(subject, content, done) { + var pusher = new pushbullet(pbConf.key); + pusher.note(pbConf.email, subject, content, function(error, response) { + if (error || !response) { + log.error('Pushbullet ERROR:', error) + } else if (response && response.active) { + log.info('Pushbullet Message Sent') + } + }); +}; + +Pushbullet.prototype.checkResults = function(err) { + if (err) + log.warn('error sending pushbullet message', err); + else + log.info('Send advice via pushbullet message.'); +}; + +module.exports = Pushbullet; \ No newline at end of file diff --git a/plugins/pushover.js b/plugins/pushover.js new file mode 100644 index 0000000..096a7a0 --- /dev/null +++ b/plugins/pushover.js @@ -0,0 +1,85 @@ +var push = require( 'pushover-notifications' ); +var _ = require('lodash'); +var log = require('../core/log.js'); +var util = require('../core/util.js'); +var config = util.getConfig(); +var pushoverConfig = config.pushover; + +var Pushover = function() { + _.bindAll(this); + + this.p; + this.price = 'N/A'; + + this.setup(); +} + +Pushover.prototype.setup = function() { + var setupPushover = function() { + this.p = new push( { + user: pushoverConfig.user, + token: pushoverConfig.key, + }); + + if(pushoverConfig.sendPushoverOnStart) { + this.send( + "Gekko has started", + [ + "I've just started watching ", + config.watch.exchange, + ' ', + config.watch.currency, + '/', + config.watch.asset, + ". I'll let you know when I got some advice" + ].join('') + ); + } else + log.debug('Setup pushover adviser.'); + } + setupPushover.call(this); +} + +Pushover.prototype.send = function(subject, content) { + var msg = { + // These values correspond to the parameters detailed on https://pushover.net/api + // 'message' is required. All other values are optional. + message: content, + title: pushoverConfig.tag + subject, + device: 'devicename', + priority: 1 + }; + + this.p.send( msg, function( err, result ) { + if ( err ) { + throw err; + } + + console.log( result ); + }); + +} + +Pushover.prototype.processCandle = function(candle, callback) { + this.price = candle.close; + callback(); +} + +Pushover.prototype.processAdvice = function(advice) { + if (advice.recommendation == 'soft' && pushoverConfig.muteSoft) return; + var text = [ + advice.recommendation, + this.price + ].join(' '); + var subject = text; + this.send(subject, text); +} + +Pushover.prototype.checkResults = function(err) { + if(err) + log.warn('error sending pushover', err); + else + log.info('Send advice via pushover.'); +} + +module.exports = Pushover; diff --git a/plugins/redisBeacon.js b/plugins/redisBeacon.js new file mode 100644 index 0000000..b814bec --- /dev/null +++ b/plugins/redisBeacon.js @@ -0,0 +1,67 @@ +var log = require('../core/log.js'); +var util = require('../core/util'); +var config = util.getConfig(); +var redisBeacon = config.redisBeacon; +var watch = config.watch; + +var subscriptions = require('../subscriptions'); +var _ = require('lodash'); + +var redis = require("redis"); + +var Actor = function(done) { + _.bindAll(this); + + this.market = [ + watch.exchange, + watch.currency, + watch.asset + ].join('-'); + + this.init(done); +} + +// This actor is dynamically build based on +// what the config specifies it should emit. +// +// This way we limit overhead because Gekko +// only binds to events redis is going to +// emit. + +var proto = {}; +_.each(redisBeacon.broadcast, function(e) { + // grab the corresponding subscription + var subscription = _.find(subscriptions, function(s) { return s.event === e }); + + if(!subscription) + util.die('Gekko does not know this event:' + e); + + var channel = redisBeacon.channelPrefix + subscription.event + + proto[subscription.handler] = function(message, cb) { + if(!_.isFunction(cb)) + cb = _.noop; + + this.emit(channel, { + market: this.market, + data: message + }, cb); + }; + +}, this) + +Actor.prototype = proto; + +Actor.prototype.init = function(done) { + this.client = redis.createClient(redisBeacon.port, redisBeacon.host); + this.client.on('ready', _.once(done)); +} + +Actor.prototype.emit = function(channel, message) { + log.debug('Going to publish to redis channel:', channel); + + var data = JSON.stringify(message); + this.client.publish(channel, data); +} + +module.exports = Actor; \ No newline at end of file diff --git a/plugins/slack.js b/plugins/slack.js new file mode 100644 index 0000000..9622a6d --- /dev/null +++ b/plugins/slack.js @@ -0,0 +1,96 @@ +const WebClient = require('@slack/client').WebClient; +const _ = require('lodash'); +const log = require('../core/log.js'); +const util = require('../core/util.js'); +const config = util.getConfig(); +const slackConfig = config.slack; + +const Slack = function(done) { + _.bindAll(this); + + this.slack; + this.price = 'N/A'; + + this.done = done; + this.setup(); +}; + +Slack.prototype.setup = function(done) { + this.slack = new WebClient(slackConfig.token); + + const setupSlack = function(error, result) { + if(slackConfig.sendMessageOnStart){ + const body = this.createResponse("#439FE0","Gekko started!") ; + this.send(body); + }else{ + log.debug('Skipping Send message on startup') + } + }; + setupSlack.call(this) +}; + +Slack.prototype.processCandle = function(candle, done) { + this.price = candle.close; + + done(); +}; + +Slack.prototype.processAdvice = function(advice) { + if (advice.recommendation == 'soft' && slackConfig.muteSoft) return; + + const color = advice.recommendation === "long" ? "good" : (advice.recommendation === "short" ? "danger" : "warning"); + const body = this.createResponse(color, "There is a new trend! The advice is to go `" + advice.recommendation + "`! Current price is `" + this.price + "`"); + + this.send(body); +}; + +Slack.prototype.processStratNotification = function({ content }) { + const body = this.createResponse('#909399', content); + this.send(body); +} + +Slack.prototype.send = function(content, done) { + this.slack.chat.postMessage(slackConfig.channel, "", content, (error, response) => { + if (error || !response) { + log.error('Slack ERROR:', error); + } else { + log.info('Slack Message Sent'); + } + }); +}; + +Slack.prototype.checkResults = function(error) { + if (error) { + log.warn('error sending slack', error); + } else { + log.info('Send advice via slack.'); + } +}; + +Slack.prototype.createResponse = function(color, message) { + const template = { + "username": this.createUserName(), + "icon_url": this.createIconUrl(), + "attachments": [ + { + "fallback": "", + "color": color, + "text": message, + "mrkdwn_in": ["text"] + } + ] + }; + + return template; +}; + +Slack.prototype.createUserName = function() { + return config.watch.exchange[0].toUpperCase() + config.watch.exchange.slice(1) + " - " + config.watch.currency + "/" + config.watch.asset; +}; + +Slack.prototype.createIconUrl = function() { + const asset = config.watch.asset === "XBT" ? "btc" :config.watch.asset.toLowerCase(); + return "https://github.com/cjdowner/cryptocurrency-icons/raw/master/128/icon/" + asset + ".png"; +}; + +module.exports = Slack; diff --git a/plugins/sqlite/handle.js b/plugins/sqlite/handle.js new file mode 100644 index 0000000..93832ee --- /dev/null +++ b/plugins/sqlite/handle.js @@ -0,0 +1,58 @@ +var _ = require('lodash'); +var fs = require('fs'); + +var util = require('../../core/util.js'); +var config = util.getConfig(); +var dirs = util.dirs(); + +var adapter = config.sqlite; + +// verify the correct dependencies are installed +var pluginHelper = require(dirs.core + 'pluginUtil'); +var pluginMock = { + slug: 'sqlite adapter', + dependencies: adapter.dependencies, +}; + +var cannotLoad = pluginHelper.cannotLoad(pluginMock); +if (cannotLoad) util.die(cannotLoad); + +// should be good now +if (config.debug) var sqlite3 = require('sqlite3').verbose(); +else var sqlite3 = require('sqlite3'); + +var plugins = require(util.dirs().gekko + 'plugins'); + +var version = adapter.version; + +var dbName = config.watch.exchange.toLowerCase() + '_' + version + '.db'; +var dir = dirs.gekko + adapter.dataDirectory; + +var fullPath = [dir, dbName].join('/'); + +var mode = util.gekkoMode(); +if (mode === 'realtime' || mode === 'importer') { + if (!fs.existsSync(dir)) fs.mkdirSync(dir); +} else if (mode === 'backtest') { + if (!fs.existsSync(dir)) util.die('History directory does not exist.'); + + if (!fs.existsSync(fullPath)) + util.die( + `History database does not exist for exchange ${ + config.watch.exchange + } at version ${version}.` + ); +} + +module.exports = { + initDB: () => { + var journalMode = config.sqlite.journalMode || 'PERSIST'; + var syncMode = journalMode === 'WAL' ? 'NORMAL' : 'FULL'; + + var db = new sqlite3.Database(fullPath); + db.run('PRAGMA synchronous = ' + syncMode); + db.run('PRAGMA journal_mode = ' + journalMode); + db.configure('busyTimeout', 10000); + return db; + } +}; diff --git a/plugins/sqlite/reader.js b/plugins/sqlite/reader.js new file mode 100644 index 0000000..a94bf0e --- /dev/null +++ b/plugins/sqlite/reader.js @@ -0,0 +1,169 @@ +var _ = require('lodash'); +var util = require('../../core/util.js'); +var config = util.getConfig(); +var log = require(util.dirs().core + 'log'); + +var sqlite = require('./handle'); +var sqliteUtil = require('./util'); + +var Reader = function() { + _.bindAll(this); + this.db = sqlite.initDB(true); +} + + +// returns the most recent window complete candle +// windows within `from` and `to` +Reader.prototype.mostRecentWindow = function(from, to, next) { + to = to.unix(); + from = from.unix(); + + var maxAmount = to - from + 1; + + this.db.all(` + SELECT start from ${sqliteUtil.table('candles')} + WHERE start <= ${to} AND start >= ${from} + ORDER BY start DESC + `, function(err, rows) { + if(err) { + + // bail out if the table does not exist + if(err.message.split(':')[1] === ' no such table') + return next(false); + + log.error(err); + return util.die('DB error while reading mostRecentWindow'); + } + + // no candles are available + if(rows.length === 0) { + return next(false); + } + + if(rows.length === maxAmount) { + + // full history is available! + + return next({ + from: from, + to: to + }); + } + + // we have at least one gap, figure out where + var mostRecent = _.first(rows).start; + + var gapIndex = _.findIndex(rows, function(r, i) { + return r.start !== mostRecent - i * 60; + }); + + // if there was no gap in the records, but + // there were not enough records. + if(gapIndex === -1) { + var leastRecent = _.last(rows).start; + return next({ + from: leastRecent, + to: mostRecent + }); + } + + // else return mostRecent and the + // the minute before the gap + return next({ + from: rows[ gapIndex - 1 ].start, + to: mostRecent + }); + + }) +} + +Reader.prototype.tableExists = function(name, next) { + + this.db.all(` + SELECT name FROM sqlite_master WHERE type='table' AND name='${sqliteUtil.table(name)}'; + `, function(err, rows) { + if(err) { + console.error(err); + return util.die('DB error at `get`'); + } + + next(null, rows.length === 1); + }); +} + +Reader.prototype.get = function(from, to, what, next) { + if(what === 'full') + what = '*'; + + this.db.all(` + SELECT ${what} from ${sqliteUtil.table('candles')} + WHERE start <= ${to} AND start >= ${from} + ORDER BY start ASC + `, function(err, rows) { + if(err) { + console.error(err); + return util.die('DB error at `get`'); + } + + next(null, rows); + }); +} + +Reader.prototype.count = function(from, to, next) { + this.db.all(` + SELECT COUNT(*) as count from ${sqliteUtil.table('candles')} + WHERE start <= ${to} AND start >= ${from} + `, function(err, res) { + if(err) { + console.error(err); + return util.die('DB error at `get`'); + } + + next(null, _.first(res).count); + }); +} + +Reader.prototype.countTotal = function(next) { + this.db.all(` + SELECT COUNT(*) as count from ${sqliteUtil.table('candles')} + `, function(err, res) { + if(err) { + console.error(err); + return util.die('DB error at `get`'); + } + + next(null, _.first(res).count); + }); +} + +Reader.prototype.getBoundry = function(next) { + + this.db.all(` + SELECT + ( + SELECT start + FROM ${sqliteUtil.table('candles')} + ORDER BY start LIMIT 1 + ) as 'first', + ( + SELECT start + FROM ${sqliteUtil.table('candles')} + ORDER BY start DESC + LIMIT 1 + ) as 'last' + `, function(err, rows) { + if(err) { + console.error(err); + return util.die('DB error at `get`'); + } + + next(null, _.first(rows)); + }); +} + +Reader.prototype.close = function() { + this.db.close(); + this.db = null; +} + +module.exports = Reader; \ No newline at end of file diff --git a/plugins/sqlite/scanner.js b/plugins/sqlite/scanner.js new file mode 100644 index 0000000..9c7d81b --- /dev/null +++ b/plugins/sqlite/scanner.js @@ -0,0 +1,64 @@ +const _ = require('lodash'); +const async = require('async'); +const fs = require('fs'); + +const util = require('../../core/util.js'); +const config = util.getConfig(); +const dirs = util.dirs(); + +const sqlite3 = require('sqlite3'); + +// todo: rewrite with generators or async/await.. +module.exports = done => { + const dbDirectory = dirs.gekko + config.sqlite.dataDirectory + + if(!fs.existsSync(dbDirectory)) + return done(null, []); + + const files = fs.readdirSync(dbDirectory); + + const dbs = files + .filter(f => { + let parts = f.split('.'); + if(_.last(parts) === 'db') + return true; + }) + + if(!_.size(dbs)) + return done(null, []); + + let markets = []; + + async.each(dbs, (db, next) => { + + const exchange = _.first(db.split('_')); + const handle = new sqlite3.Database(dbDirectory + '/' + db, sqlite3.OPEN_READONLY, err => { + if(err) + return next(err); + + handle.all(`SELECT name FROM sqlite_master WHERE type='table'`, (err, tables) => { + if(err) + return next(err); + + _.each(tables, table => { + let parts = table.name.split('_'); + let first = parts.shift(); + if(first === 'candles') + markets.push({ + exchange: exchange, + currency: _.first(parts), + asset: _.last(parts) + }); + }); + + next(); + }); + }); + + + }, + // got all tables! + err => { + done(err, markets); + }); +} \ No newline at end of file diff --git a/plugins/sqlite/util.js b/plugins/sqlite/util.js new file mode 100644 index 0000000..bedb57e --- /dev/null +++ b/plugins/sqlite/util.js @@ -0,0 +1,15 @@ +var config = require('../../core/util.js').getConfig(); + +var watch = config.watch; +var settings = { + exchange: watch.exchange, + pair: [watch.currency, watch.asset], + historyPath: config.sqlite.dataDirectory +} + +module.exports = { + settings: settings, + table: function(name) { + return [name, settings.pair.join('_')].join('_'); + } +} \ No newline at end of file diff --git a/plugins/sqlite/writer.js b/plugins/sqlite/writer.js new file mode 100644 index 0000000..34aab30 --- /dev/null +++ b/plugins/sqlite/writer.js @@ -0,0 +1,128 @@ +var _ = require('lodash'); +var config = require('../../core/util.js').getConfig(); + +var sqlite = require('./handle'); +var sqliteUtil = require('./util'); +var util = require('../../core/util'); +var log = require('../../core/log'); + +var Store = function(done, pluginMeta) { + _.bindAll(this); + this.done = done; + + this.db = sqlite.initDB(false); + this.db.serialize(this.upsertTables); + + this.cache = []; + this.buffered = util.gekkoMode() === "importer"; +} + +Store.prototype.upsertTables = function() { + var createQueries = [ + ` + CREATE TABLE IF NOT EXISTS + ${sqliteUtil.table('candles')} ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + start INTEGER UNIQUE, + open REAL NOT NULL, + high REAL NOT NULL, + low REAL NOT NULL, + close REAL NOT NULL, + vwp REAL NOT NULL, + volume REAL NOT NULL, + trades INTEGER NOT NULL + ); + `, + + // TODO: create trades + // `` + + // TODO: create advices + // `` + ]; + + var next = _.after(_.size(createQueries), this.done); + + _.each(createQueries, function(q) { + this.db.run(q, next); + }, this); +} + +Store.prototype.writeCandles = function() { + if(_.isEmpty(this.cache)) + return; + + const transaction = () => { + this.db.run("BEGIN TRANSACTION"); + + var stmt = this.db.prepare(` + INSERT OR IGNORE INTO ${sqliteUtil.table('candles')} + VALUES (?,?,?,?,?,?,?,?,?) + `, function(err, rows) { + if(err) { + log.error(err); + return util.die('DB error at INSERT: '+ err); + } + }); + + _.each(this.cache, candle => { + stmt.run( + null, + candle.start.unix(), + candle.open, + candle.high, + candle.low, + candle.close, + candle.vwp, + candle.volume, + candle.trades + ); + }); + + stmt.finalize(); + this.db.run("COMMIT"); + // TEMP: should fix https://forum.gekko.wizb.it/thread-57279-post-59194.html#pid59194 + this.db.run("pragma wal_checkpoint;"); + + this.cache = []; + } + + this.db.serialize(transaction); +} + +var processCandle = function(candle, done) { + this.cache.push(candle); + if (!this.buffered || this.cache.length > 1000) + this.writeCandles(); + + done(); +}; + +var finalize = function(done) { + this.writeCandles(); + this.db.close(() => { done(); }); + this.db = null; +} + +if(config.candleWriter.enabled) { + Store.prototype.processCandle = processCandle; + Store.prototype.finalize = finalize; +} + +// TODO: add storing of trades / advice? + +// var processTrades = function(candles) { +// util.die('NOT IMPLEMENTED'); +// } + +// var processAdvice = function(candles) { +// util.die('NOT IMPLEMENTED'); +// } + +// if(config.tradeWriter.enabled) +// Store.prototype.processTrades = processTrades; + +// if(config.adviceWriter.enabled) +// Store.prototype.processAdvice = processAdvice; + +module.exports = Store; diff --git a/plugins/telegrambot.js b/plugins/telegrambot.js new file mode 100644 index 0000000..7fe18fb --- /dev/null +++ b/plugins/telegrambot.js @@ -0,0 +1,197 @@ +const log = require('../core/log'); +const moment = require('moment'); +const _ = require('lodash'); +const config = require('../core/util').getConfig(); +const telegrambot = config.telegrambot; +const emitTrades = telegrambot.emitTrades; +const utc = moment.utc; +const telegram = require("node-telegram-bot-api"); + +const Actor = function() { + _.bindAll(this); + + this.advice = null; + this.adviceTime = utc(); + + this.price = 'Dont know yet :('; + this.priceTime = utc(); + + this.commands = { + '/start': 'emitStart', + '/advice': 'emitAdvice', + '/subscribe': 'emitSubscribe', + '/unsubscribe': 'emitUnSubscribe', + '/price': 'emitPrice', + '/help': 'emitHelp' + }; + if (telegrambot.donate) { + this.commands['/donate'] = 'emitDonate'; + } + this.rawCommands = _.keys(this.commands); + this.chatId = null; + this.subscribers = []; + this.bot = new telegram(telegrambot.token, { polling: true }); + this.bot.onText(/(.+)/, this.verifyQuestion); +}; + +Actor.prototype.processCandle = function(candle, done) { + this.price = candle.close; + this.priceTime = candle.start; + + done(); +}; + +Actor.prototype.processAdvice = function(advice) { + if (advice.recommendation === 'soft') return; + this.advice = advice.recommendation; + this.adviceTime = utc(); + this.advicePrice = this.price; + this.subscribers.forEach(this.emitAdvice, this); +}; + +if(emitTrades) { + Actor.prototype.processTradeInitiated = function (tradeInitiated) { + var message = 'Trade initiated. ID: ' + tradeInitiated.id + + '\nAction: ' + tradeInitiated.action + '\nPortfolio: ' + + tradeInitiated.portfolio + '\nBalance: ' + tradeInitiated.balance; + this.bot.sendMessage(this.chatId, message); + } + + Actor.prototype.processTradeCancelled = function (tradeCancelled) { + var message = 'Trade cancelled. ID: ' + tradeCancelled.id; + this.bot.sendMessage(this.chatId, message); + } + + Actor.prototype.processTradeAborted = function (tradeAborted) { + var message = 'Trade aborted. ID: ' + tradeAborted.id + + '\nNot creating order! Reason: ' + tradeAborted.reason; + this.bot.sendMessage(this.chatId, message); + } + + Actor.prototype.processTradeErrored = function (tradeErrored) { + var message = 'Trade errored. ID: ' + tradeErrored.id + + '\nReason: ' + tradeErrored.reason; + this.bot.sendMessage(this.chatId, message); + } + + Actor.prototype.processTradeCompleted = function (tradeCompleted) { + var message = 'Trade completed. ID: ' + tradeCompleted.id + + '\nAction: ' + tradeCompleted.action + + '\nPrice: ' + tradeCompleted.price + + '\nAmount: ' + tradeCompleted.amount + + '\nCost: ' + tradeCompleted.cost + + '\nPortfolio: ' + tradeCompleted.portfolio + + '\nBalance: ' + tradeCompleted.balance + + '\nFee percent: ' + tradeCompleted.feePercent + + '\nEffective price: ' + tradeCompleted.effectivePrice; + this.bot.sendMessage(this.chatId, message); + } +} + +Actor.prototype.verifyQuestion = function(msg, text) { + this.chatId = msg.chat.id; + if (text[1].toLowerCase() in this.commands) { + this[this.commands[text[1].toLowerCase()]](); + } else { + this.emitHelp(); + } +}; + +Actor.prototype.emitStart = function() { + this.bot.sendMessage(this.chatId, 'Hello! How can I help you?'); +}; + +Actor.prototype.emitSubscribe = function() { + if (this.subscribers.indexOf(this.chatId) === -1) { + this.subscribers.push(this.chatId); + this.bot.sendMessage(this.chatId, `Success! Got ${this.subscribers.length} subscribers.`); + } else { + this.bot.sendMessage(this.chatId, "You are already subscribed."); + } +}; + +Actor.prototype.emitUnSubscribe = function() { + if (this.subscribers.indexOf(this.chatId) > -1) { + this.subscribers.splice(this.subscribers.indexOf(this.chatId), 1); + this.bot.sendMessage(this.chatId, "Success!"); + } else { + this.bot.sendMessage(this.chatId, "You are not subscribed."); + } +}; + +Actor.prototype.emitAdvice = function(chatId) { + let message = [ + 'Advice for ', + config.watch.exchange, + ' ', + config.watch.currency, + '/', + config.watch.asset, + ' using ', + config.tradingAdvisor.method, + ' at ', + config.tradingAdvisor.candleSize, + ' minute candles, is:\n', + ].join(''); + if (this.advice) { + message += this.advice + + ' ' + + config.watch.asset + + ' ' + + this.advicePrice + + ' (' + + this.adviceTime.fromNow() + + ')'; + } else { + message += 'None' + } + + if (chatId) { + this.bot.sendMessage(chatId, message); + } else { + this.bot.sendMessage(this.chatId, message); + } +}; + +// sent price over to the last chat +Actor.prototype.emitPrice = function() { + const message = [ + 'Current price at ', + config.watch.exchange, + ' ', + config.watch.currency, + '/', + config.watch.asset, + ' is ', + this.price, + ' ', + config.watch.currency, + ' (from ', + this.priceTime.fromNow(), + ')' + ].join(''); + + this.bot.sendMessage(this.chatId, message); +}; + +Actor.prototype.emitDonate = function() { + this.bot.sendMessage(this.chatId, telegrambot.donate); +}; + +Actor.prototype.emitHelp = function() { + let message = _.reduce( + this.rawCommands, + function(message, command) { + return message + ' ' + command + ','; + }, + 'Possible commands are:' + ); + message = message.substr(0, _.size(message) - 1) + '.'; + this.bot.sendMessage(this.chatId, message); +}; + +Actor.prototype.logError = function(message) { + log.error('Telegram ERROR:', message); +}; + +module.exports = Actor; diff --git a/plugins/trader/trader.js b/plugins/trader/trader.js new file mode 100644 index 0000000..1ee4b32 --- /dev/null +++ b/plugins/trader/trader.js @@ -0,0 +1,412 @@ +const _ = require('lodash'); +const util = require('../../core/util.js'); +const config = util.getConfig(); +const dirs = util.dirs(); +const moment = require('moment'); + +const log = require(dirs.core + 'log'); +const Broker = require(dirs.broker + '/gekkoBroker'); + +require(dirs.gekko + '/exchange/dependencyCheck'); + +const Trader = function(next) { + + _.bindAll(this); + + this.brokerConfig = { + ...config.trader, + ...config.watch, + private: true + } + + this.propogatedTrades = 0; + this.propogatedTriggers = 0; + + 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'); + } + + this.sync(() => { + log.info('\t', 'Portfolio:'); + log.info('\t\t', this.portfolio.currency, this.brokerConfig.currency); + log.info('\t\t', this.portfolio.asset, this.brokerConfig.asset); + log.info('\t', 'Balance:'); + log.info('\t\t', this.balance, this.brokerConfig.currency); + log.info('\t', 'Exposed:'); + log.info('\t\t', + this.exposed ? 'yes' : 'no', + `(${(this.exposure * 100).toFixed(2)}%)` + ); + next(); + }); + + this.cancellingOrder = false; + this.sendInitialPortfolio = false; + + setInterval(this.sync, 1000 * 60 * 10); +} + +// teach our trader events +util.makeEventEmitter(Trader); + +Trader.prototype.sync = function(next) { + log.debug('syncing private data'); + this.broker.syncPrivateData(() => { + if(!this.price) { + this.price = this.broker.ticker.bid; + } + + const oldPortfolio = this.portfolio; + + this.setPortfolio(); + this.setBalance(); + + if(this.sendInitialPortfolio && !_.isEqual(oldPortfolio, this.portfolio)) { + this.relayPortfolioChange(); + } + + // balance is relayed every minute + // no need to do it here. + + if(next) { + next(); + } + }); +} + +Trader.prototype.relayPortfolioChange = function() { + this.deferredEmit('portfolioChange', { + asset: this.portfolio.asset, + currency: this.portfolio.currency + }); +} + +Trader.prototype.relayPortfolioValueChange = function() { + this.deferredEmit('portfolioValueChange', { + balance: this.balance + }); +} + +Trader.prototype.setPortfolio = function() { + this.portfolio = { + currency: _.find( + this.broker.portfolio.balances, + b => b.name === this.brokerConfig.currency + ).amount, + asset: _.find( + this.broker.portfolio.balances, + b => b.name === this.brokerConfig.asset + ).amount + } +} + +Trader.prototype.setBalance = function() { + this.balance = this.portfolio.currency + this.portfolio.asset * this.price; + this.exposure = (this.portfolio.asset * this.price) / this.balance; + // if more than 10% of balance is in asset we are exposed + this.exposed = this.exposure > 0.1; +} + +Trader.prototype.processCandle = function(candle, done) { + this.price = candle.close; + const previousBalance = this.balance; + this.setPortfolio(); + this.setBalance(); + + if(!this.sendInitialPortfolio) { + this.sendInitialPortfolio = true; + this.deferredEmit('portfolioChange', { + asset: this.portfolio.asset, + currency: this.portfolio.currency + }); + } + + if(this.balance !== previousBalance) { + // this can happen because: + // A) the price moved and we have > 0 asset + // B) portfolio got changed + this.relayPortfolioValueChange(); + } + + done(); +} + +Trader.prototype.processAdvice = function(advice) { + let direction; + + if(advice.recommendation === 'long') { + direction = 'buy'; + } else if(advice.recommendation === 'short') { + direction = 'sell'; + } else { + log.error('ignoring advice in unknown direction'); + return; + } + + const id = 'trade-' + (++this.propogatedTrades); + + if(this.order) { + if(this.order.side === direction) { + return log.info('ignoring advice: already in the process to', direction); + } + + if(this.cancellingOrder) { + return log.info('ignoring advice: already cancelling previous', this.order.side, 'order'); + } + + log.info('Received advice to', direction, 'however Gekko is already in the process to', this.order.side); + log.info('Canceling', this.order.side, 'order first'); + return this.cancelOrder(id, advice, () => this.processAdvice(advice)); + } + + let amount; + + if(direction === 'buy') { + + if(this.exposed) { + log.info('NOT buying, already exposed'); + return this.deferredEmit('tradeAborted', { + id, + adviceId: advice.id, + action: direction, + portfolio: this.portfolio, + balance: this.balance, + reason: "Portfolio already in position." + }); + } + + amount = this.portfolio.currency / this.price * 0.95; + + log.info( + 'Trader', + 'Received advice to go long.', + 'Buying ', this.brokerConfig.asset + ); + + } else if(direction === 'sell') { + + if(!this.exposed) { + log.info('NOT selling, already no exposure'); + return this.deferredEmit('tradeAborted', { + id, + adviceId: advice.id, + action: direction, + portfolio: this.portfolio, + balance: this.balance, + reason: "Portfolio already in position." + }); + } + + // clean up potential old stop trigger + if(this.activeStopTrigger) { + this.deferredEmit('triggerAborted', { + id: this.activeStopTrigger.id, + date: advice.date + }); + + this.activeStopTrigger.instance.cancel(); + + delete this.activeStopTrigger; + } + + amount = this.portfolio.asset; + + log.info( + 'Trader', + 'Received advice to go short.', + 'Selling ', this.brokerConfig.asset + ); + } + + this.createOrder(direction, amount, advice, id); +} + +Trader.prototype.createOrder = function(side, amount, advice, id) { + const type = 'sticky'; + + // NOTE: this is the best check we can do at this point + // with the best price we have. The order won't be actually + // created with this.price, but it should be close enough to + // catch non standard errors (lot size, price filter) on + // exchanges that have them. + const check = this.broker.isValidOrder(amount, this.price); + + if(!check.valid) { + log.warn('NOT creating order! Reason:', check.reason); + return this.deferredEmit('tradeAborted', { + id, + adviceId: advice.id, + action: side, + portfolio: this.portfolio, + balance: this.balance, + reason: check.reason + }); + } + + log.debug('Creating order to', side, amount, this.brokerConfig.asset); + + this.deferredEmit('tradeInitiated', { + id, + adviceId: advice.id, + action: side, + portfolio: this.portfolio, + balance: this.balance + }); + + this.order = this.broker.createOrder(type, side, amount); + + this.order.on('fill', f => log.info('[ORDER] partial', side, 'fill, total filled:', f)); + this.order.on('statusChange', s => log.debug('[ORDER] statusChange:', s)); + + this.order.on('error', e => { + log.error('[ORDER] Gekko received error from GB:', e.message); + log.debug(e); + this.order = null; + this.cancellingOrder = false; + + this.deferredEmit('tradeErrored', { + id, + adviceId: advice.id, + date: moment(), + reason: e.message + }); + + }); + 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(() => { + + let cost; + if(_.isNumber(summary.feePercent)) { + cost = summary.feePercent / 100 * summary.amount * summary.price; + } + + let effectivePrice; + if(_.isNumber(summary.feePercent)) { + if(side === 'buy') { + effectivePrice = summary.price * (1 + summary.feePercent / 100); + } else { + effectivePrice = summary.price * (1 - summary.feePercent / 100); + } + } else { + log.warn('WARNING: exchange did not provide fee information, assuming no fees..'); + effectivePrice = summary.price; + } + + this.deferredEmit('tradeCompleted', { + id, + adviceId: advice.id, + action: summary.side, + cost, + amount: summary.amount, + price: summary.price, + portfolio: this.portfolio, + balance: this.balance, + date: summary.date, + feePercent: summary.feePercent, + effectivePrice + }); + + if( + side === 'buy' && + advice.trigger && + advice.trigger.type === 'trailingStop' + ) { + const trigger = advice.trigger; + const triggerId = 'trigger-' + (++this.propogatedTriggers); + + this.deferredEmit('triggerCreated', { + id: triggerId, + at: advice.date, + type: 'trailingStop', + properties: { + trail: trigger.trailValue, + initialPrice: summary.price, + } + }); + + log.info(`Creating trailingStop trigger "${triggerId}"! Properties:`); + log.info(`\tInitial price: ${summary.price}`); + log.info(`\tTrail of: ${trigger.trailValue}`); + + this.activeStopTrigger = { + id: triggerId, + adviceId: advice.id, + instance: this.broker.createTrigger({ + type: 'trailingStop', + onTrigger: this.onStopTrigger, + props: { + trail: trigger.trailValue, + initialPrice: summary.price, + } + }) + } + } + }); + }) + }); +} + +Trader.prototype.onStopTrigger = function(price) { + log.info(`TrailingStop trigger "${this.activeStopTrigger.id}" fired! Observed price was ${price}`); + + this.deferredEmit('triggerFired', { + id: this.activeStopTrigger.id, + date: moment() + }); + + const adviceMock = { + recommendation: 'short', + id: this.activeStopTrigger.adviceId + } + + delete this.activeStopTrigger; + + this.processAdvice(adviceMock); +} + +Trader.prototype.cancelOrder = function(id, advice, next) { + + if(!this.order) { + return next(); + } + + this.cancellingOrder = true; + + this.order.removeAllListeners(); + this.order.cancel(); + this.order.once('completed', () => { + this.order = null; + this.cancellingOrder = false; + this.deferredEmit('tradeCancelled', { + id, + adviceId: advice.id, + date: moment() + }); + this.sync(next); + }); +} + +module.exports = Trader; diff --git a/plugins/tradingAdvisor/asyncIndicatorRunner.js b/plugins/tradingAdvisor/asyncIndicatorRunner.js new file mode 100644 index 0000000..da632a8 --- /dev/null +++ b/plugins/tradingAdvisor/asyncIndicatorRunner.js @@ -0,0 +1,150 @@ +const _ = require('lodash'); +const fs = require('fs'); +const util = require('../../core/util'); +const config = util.getConfig(); +const dirs = util.dirs(); +const log = require(dirs.core + 'log'); + +const talib = require(dirs.core + 'talib'); +const tulind = require(dirs.core + 'tulind'); + +const allowedTalibIndicators = _.keys(talib); +const allowedTulipIndicators = _.keys(tulind); + +const AsyncIndicatorRunner = function() { + this.talibIndicators = {}; + this.tulipIndicators = {}; + + this.candleProps = { + open: [], + high: [], + low: [], + close: [], + volume: [] + }; + + this.candlePropsCacheSize = 1000; + + this.inflight = false; + this.backlog = []; + this.age = 0; +} + +AsyncIndicatorRunner.prototype.processCandle = function(candle, next) { + if(this.inflight) { + return this.backlog.push({candle, next}); + } + + this.age++; + this.inflight = true; + + this.candleProps.open.push(candle.open); + this.candleProps.high.push(candle.high); + this.candleProps.low.push(candle.low); + this.candleProps.close.push(candle.close); + this.candleProps.volume.push(candle.volume); + + if(this.age > this.candlePropsCacheSize) { + this.candleProps.open.shift(); + this.candleProps.high.shift(); + this.candleProps.low.shift(); + this.candleProps.close.shift(); + this.candleProps.volume.shift(); + } + + this.calculateIndicators(next); +} + +AsyncIndicatorRunner.prototype.calculateIndicators = function(next) { + const done = _.after( + _.size(this.talibIndicators) + _.size(this.tulipIndicators), + this.handlePostFlight(next) + ); + + // handle result from talib + const talibResultHander = name => (err, result) => { + if(err) + util.die('TALIB ERROR:', err); + + this.talibIndicators[name].result = _.mapValues(result, v => _.last(v)); + done(); + } + + // handle result from talib + _.each( + this.talibIndicators, + (indicator, name) => indicator.run( + this.candleProps, + talibResultHander(name) + ) + ); + + // handle result from tulip + var tulindResultHander = name => (err, result) => { + if(err) + util.die('TULIP ERROR:', err); + + this.tulipIndicators[name].result = _.mapValues(result, v => _.last(v)); + done(); + } + + // handle result from tulip indicators + _.each( + this.tulipIndicators, + (indicator, name) => indicator.run( + this.candleProps, + tulindResultHander(name) + ) + ); +} + +AsyncIndicatorRunner.prototype.handlePostFlight = function(next) { + return () => { + next(); + this.inflight = false; + + if(this.backlog.length) { + const { candle, next } = this.backlog.shift(); + this.processCandle(candle, next); + } + } +} + +AsyncIndicatorRunner.prototype.addTalibIndicator = function(name, type, parameters) { + if(!talib) + util.die('Talib is not enabled'); + + if(!_.contains(allowedTalibIndicators, type)) + util.die('I do not know the talib indicator ' + type); + + if(this.setup) + util.die('Can only add talib indicators in the init method!'); + + var basectx = this; + + this.talibIndicators[name] = { + run: talib[type].create(parameters), + result: NaN + } +} + +AsyncIndicatorRunner.prototype.addTulipIndicator = function(name, type, parameters) { + if(!tulind) { + util.die('Tulip indicators is not enabled'); + } + + if(!_.contains(allowedTulipIndicators, type)) + util.die('I do not know the tulip indicator ' + type); + + if(this.setup) + util.die('Can only add tulip indicators in the init method!'); + + var basectx = this; + + this.tulipIndicators[name] = { + run: tulind[type].create(parameters), + result: NaN + } +} + +module.exports = AsyncIndicatorRunner; \ No newline at end of file diff --git a/plugins/tradingAdvisor/baseTradingMethod.js b/plugins/tradingAdvisor/baseTradingMethod.js new file mode 100644 index 0000000..903b24c --- /dev/null +++ b/plugins/tradingAdvisor/baseTradingMethod.js @@ -0,0 +1,334 @@ +const _ = require('lodash'); +const fs = require('fs'); +const util = require('../../core/util'); +const config = util.getConfig(); +const dirs = util.dirs(); +const log = require(dirs.core + 'log'); + +const ENV = util.gekkoEnv(); +const mode = util.gekkoMode(); +const startTime = util.getStartTime(); + +const indicatorsPath = dirs.methods + 'indicators/'; +const indicatorFiles = fs.readdirSync(indicatorsPath); +const Indicators = {}; + +const AsyncIndicatorRunner = require('./asyncIndicatorRunner'); + +_.each(indicatorFiles, function(indicator) { + const indicatorName = indicator.split(".")[0]; + if (indicatorName[0] != "_") + try { + Indicators[indicatorName] = require(indicatorsPath + indicator); + } catch (e) { + log.error("Failed to load indicator", indicatorName); + } +}); + +const allowedIndicators = _.keys(Indicators); + +var Base = function(settings) { + _.bindAll(this); + + // properties + this.age = 0; + this.processedTicks = 0; + this.setup = false; + this.settings = settings; + this.tradingAdvisor = config.tradingAdvisor; + // defaults + this.priceValue = 'close'; + this.indicators = {}; + this.asyncTick = false; + this.deferredTicks = []; + + this.propogatedAdvices = 0; + + this.completedWarmup = false; + + this.asyncIndicatorRunner = new AsyncIndicatorRunner(); + + this._currentDirection; + + // make sure we have all methods + _.each(['init', 'check'], function(fn) { + if(!this[fn]) + util.die('No ' + fn + ' function in this strategy found.') + }, this); + + if(!this.update) + this.update = function() {}; + + if(!this.end) + this.end = function() {}; + + if(!this.onTrade) + this.onTrade = function() {}; + + // let's run the implemented starting point + this.init(); + + if(_.isNumber(this.requiredHistory)) { + log.debug('Ignoring strategy\'s required history, using the "config.tradingAdvisor.historySize" instead.'); + } + this.requiredHistory = config.tradingAdvisor.historySize; + + if(!config.debug || !this.log) + this.log = function() {}; + + this.setup = true; + + if(_.size(this.asyncIndicatorRunner.talibIndicators) || _.size(this.asyncIndicatorRunner.tulipIndicators)) + this.asyncTick = true; + else + delete this.asyncIndicatorRunner; +} + +// teach our base trading method events +util.makeEventEmitter(Base); + +Base.prototype.tick = function(candle, done) { + this.age++; + + const afterAsync = () => { + this.calculateSyncIndicators(candle, done); + } + + if(this.asyncTick) { + this.asyncIndicatorRunner.processCandle(candle, () => { + + if(!this.talibIndicators) { + this.talibIndicators = this.asyncIndicatorRunner.talibIndicators; + this.tulipIndicators = this.asyncIndicatorRunner.tulipIndicators; + } + + afterAsync(); + }); + } else { + afterAsync(); + } +} + +Base.prototype.isBusy = function() { + if(!this.asyncTick) + return false; + + return this.asyncIndicatorRunner.inflight; +} + +Base.prototype.calculateSyncIndicators = function(candle, done) { + // update all indicators + var price = candle[this.priceValue]; + _.each(this.indicators, function(i) { + if(i.input === 'price') + i.update(price); + if(i.input === 'candle') + i.update(candle); + },this); + + this.propogateTick(candle); + + return done(); +} + +Base.prototype.propogateTick = function(candle) { + this.candle = candle; + this.update(candle); + + this.processedTicks++; + var isAllowedToCheck = this.requiredHistory <= this.age; + + if(!this.completedWarmup) { + + // in live mode we might receive more candles + // than minimally needed. In that case check + // whether candle start time is > startTime + var isPremature = false; + + if(mode === 'realtime') { + const startTimeMinusCandleSize = startTime + .clone() + .subtract(this.tradingAdvisor.candleSize, "minutes"); + + isPremature = candle.start < startTimeMinusCandleSize; + } + + if(isAllowedToCheck && !isPremature) { + this.completedWarmup = true; + this.emit( + 'stratWarmupCompleted', + {start: candle.start.clone()} + ); + } + } + + if(this.completedWarmup) { + this.log(candle); + this.check(candle); + + if( + this.asyncTick && + this.hasSyncIndicators && + this.deferredTicks.length + ) { + return this.tick(this.deferredTicks.shift()) + } + } + + const indicators = {}; + _.each(this.indicators, (indicator, name) => { + indicators[name] = indicator.result; + }); + + _.each(this.tulipIndicators, (indicator, name) => { + indicators[name] = indicator.result.result + ? indicator.result.result + : indicator.result; + }); + + _.each(this.talibIndicators, (indicator, name) => { + indicators[name] = indicator.result.outReal + ? indicator.result.outReal + : indicator.result; + }); + + this.emit('stratUpdate', { + date: candle.start.clone(), + indicators + }); + + // are we totally finished? + const completed = this.age === this.processedTicks; + if(completed && this.finishCb) + this.finishCb(); +} + +Base.prototype.processTrade = function(trade) { + if( + this._pendingTriggerAdvice && + trade.action === 'sell' && + this._pendingTriggerAdvice === trade.adviceId + ) { + // This trade came from a trigger of the previous advice, + // update stored direction + this._currentDirection = 'short'; + this._pendingTriggerAdvice = null; + } + + this.onTrade(trade); +} + +Base.prototype.addTalibIndicator = function(name, type, parameters) { + this.asyncIndicatorRunner.addTalibIndicator(name, type, parameters); +} + +Base.prototype.addTulipIndicator = function(name, type, parameters) { + this.asyncIndicatorRunner.addTulipIndicator(name, type, parameters); +} + +Base.prototype.addIndicator = function(name, type, parameters) { + if(!_.contains(allowedIndicators, type)) + util.die('I do not know the indicator ' + type); + + if(this.setup) + util.die('Can only add indicators in the init method!'); + + return this.indicators[name] = new Indicators[type](parameters); + + // some indicators need a price stream, others need full candles +} + +Base.prototype.advice = function(newDirection) { + // ignore legacy soft advice + if(!newDirection) { + return; + } + + let trigger; + if(_.isObject(newDirection)) { + if(!_.isString(newDirection.direction)) { + log.error('Strategy emitted unparsable advice:', newDirection); + return; + } + + if(newDirection.direction === this._currentDirection) { + return; + } + + if(_.isObject(newDirection.trigger)) { + if(newDirection.direction !== 'long') { + log.warn( + 'Strategy adviced a stop on not long, this is not supported.', + 'As such the stop is ignored' + ); + } else { + + // the trigger is implemented in a trader + trigger = newDirection.trigger; + + if(trigger.trailPercentage && !trigger.trailValue) { + trigger.trailValue = trigger.trailPercentage / 100 * this.candle.close; + log.info('[StratRunner] Trailing stop trail value specified as percentage, setting to:', trigger.trailValue); + } + } + } + + newDirection = newDirection.direction; + } + + if(newDirection === this._currentDirection) { + return; + } + + if(newDirection === 'short' && this._pendingTriggerAdvice) { + this._pendingTriggerAdvice = null; + } + + this._currentDirection = newDirection; + + this.propogatedAdvices++; + + const advice = { + id: 'advice-' + this.propogatedAdvices, + recommendation: newDirection + }; + + if(trigger) { + advice.trigger = trigger; + this._pendingTriggerAdvice = 'advice-' + this.propogatedAdvices; + } else { + this._pendingTriggerAdvice = null; + } + + this.emit('advice', advice); + + return this.propogatedAdvices; +} + +Base.prototype.notify = function(content) { + this.emit('stratNotification', { + content, + date: new Date(), + }) +} + +Base.prototype.finish = function(done) { + // Because the strategy might be async we need + // to be sure we only stop after all candles are + // processed. + if(!this.asyncTick) { + this.end(); + return done(); + } + + if(this.age === this.processedTicks) { + this.end(); + return done(); + } + + // we are not done, register cb + // and call after we are.. + this.finishCb = done; +} + +module.exports = Base; diff --git a/plugins/tradingAdvisor/tradingAdvisor.js b/plugins/tradingAdvisor/tradingAdvisor.js new file mode 100644 index 0000000..3c3c26c --- /dev/null +++ b/plugins/tradingAdvisor/tradingAdvisor.js @@ -0,0 +1,122 @@ +var util = require('../../core/util'); +var _ = require('lodash'); +var fs = require('fs'); +var toml = require('toml'); + +var config = util.getConfig(); +var dirs = util.dirs(); +var log = require(dirs.core + 'log'); +var CandleBatcher = require(dirs.core + 'candleBatcher'); + +var moment = require('moment'); +var isLeecher = config.market && config.market.type === 'leech'; + +var Actor = function(done) { + _.bindAll(this); + + this.done = done; + + this.batcher = new CandleBatcher(config.tradingAdvisor.candleSize); + + this.strategyName = config.tradingAdvisor.method; + + this.setupStrategy(); + + var mode = util.gekkoMode(); + + // the stitcher will try to pump in historical data + // so that the strat can use this data as a "warmup period" + // + // the realtime "leech" market won't use the stitcher + if(mode === 'realtime' && !isLeecher) { + var Stitcher = require(dirs.tools + 'dataStitcher'); + var stitcher = new Stitcher(this.batcher); + stitcher.prepareHistoricalData(done); + } else + done(); +} + +Actor.prototype.setupStrategy = function() { + + if(!fs.existsSync(dirs.methods + this.strategyName + '.js')) + util.die('Gekko can\'t find the strategy "' + this.strategyName + '"'); + + log.info('\t', 'Using the strategy: ' + this.strategyName); + + const strategy = require(dirs.methods + this.strategyName); + + // bind all trading strategy specific functions + // to the WrappedStrategy. + const WrappedStrategy = require('./baseTradingMethod'); + + _.each(strategy, function(fn, name) { + WrappedStrategy.prototype[name] = fn; + }); + + let stratSettings; + if(config[this.strategyName]) { + stratSettings = config[this.strategyName]; + } + + this.strategy = new WrappedStrategy(stratSettings); + this.strategy + .on( + 'stratWarmupCompleted', + e => this.deferredEmit('stratWarmupCompleted', e) + ) + .on('advice', this.relayAdvice) + .on( + 'stratUpdate', + e => this.deferredEmit('stratUpdate', e) + ).on('stratNotification', + e => this.deferredEmit('stratNotification', e) + ) + + this.strategy + .on('tradeCompleted', this.processTradeCompleted); + + this.batcher + .on('candle', _candle => { + const { id, ...candle } = _candle; + this.deferredEmit('stratCandle', candle); + this.emitStratCandle(candle); + }); +} + +// HANDLERS +// process the 1m candles +Actor.prototype.processCandle = function(candle, done) { + this.candle = candle; + const completedBatch = this.batcher.write([candle]); + if(completedBatch) { + this.next = done; + } else { + done(); + this.next = false; + } + this.batcher.flush(); +} + +// propogate a custom sized candle to the trading strategy +Actor.prototype.emitStratCandle = function(candle) { + const next = this.next || _.noop; + this.strategy.tick(candle, next); +} + +Actor.prototype.processTradeCompleted = function(trade) { + this.strategy.processTrade(trade); +} + +// pass through shutdown handler +Actor.prototype.finish = function(done) { + this.strategy.finish(done); +} + +// EMITTERS +Actor.prototype.relayAdvice = function(advice) { + advice.date = this.candle.start.clone().add(1, 'minute'); + this.deferredEmit('advice', advice); +} + + +module.exports = Actor; diff --git a/plugins/twitter.js b/plugins/twitter.js new file mode 100644 index 0000000..f81b98b --- /dev/null +++ b/plugins/twitter.js @@ -0,0 +1,84 @@ +var _ = require('lodash'); +var log = require('../core/log.js'); +var util = require('../core/util.js'); +var config = util.getConfig(); +var twitterConfig = config.twitter; +var TwitterApi = require('twitter'); + +require('dotenv').config() + +var Twitter = function(done) { + _.bindAll(this); + + this.twitter; + this.price = 'N/A'; + this.done = done; + this.setup(); + +}; + +Twitter.prototype.setup = function(done){ + var setupTwitter = function (err, result) { + this.client = new TwitterApi({ + consumer_key: twitterConfig.consumer_key, + consumer_secret: twitterConfig.consumer_secret, + access_token_key: twitterConfig.access_token_key, + access_token_secret: twitterConfig.access_token_secret + }); + + if(twitterConfig.sendMessageOnStart){ + var exchange = config.watch.exchange; + var currency = config.watch.currency; + var asset = config.watch.asset; + var body = "Watching " + +exchange + +" " + +currency + +" " + +asset + this.mail(body); + }else{ + log.debug('Skipping Send message on startup') + } + }; + setupTwitter.call(this) +}; + +Twitter.prototype.processCandle = function(candle, done) { + this.price = candle.close; + + done(); +}; + +Twitter.prototype.processAdvice = function(advice) { + if (advice.recommendation == "soft" && twitterConfig.muteSoft) return; + var text = [ + 'New ', config.watch.asset, ' trend. Attempting to ', + advice.recommendation == "short" ? "sell" : "buy", + ' @', + this.price, + ].join(''); + + this.mail(text); +}; + +Twitter.prototype.mail = function(content, done) { + log.info("trying to tweet"); + this.client.post('statuses/update', {status: content}, function(error, tweet, response) { + if(error || !response) { + log.error('Twitter ERROR:', error) + } else if(response && response.active){ + log.info('Twitter Message Sent') + } + }); +}; + +Twitter.prototype.checkResults = function(err) { + if(err) + log.warn('error sending email', err); + else + log.info('Send advice via email.'); +}; + + +module.exports = Twitter; diff --git a/plugins/webserver.js b/plugins/webserver.js new file mode 100644 index 0000000..c5cb8b2 --- /dev/null +++ b/plugins/webserver.js @@ -0,0 +1,27 @@ +var log = require('../core/log'); +var moment = require('moment'); +var _ = require('lodash'); +var Server = require('../web/server.js'); + +var Actor = function(next) { + _.bindAll(this); + + this.server = new Server(); + this.server.setup(next); +} + +Actor.prototype.init = function(data) { + this.server.broadcastHistory(data); +}; + +Actor.prototype.processCandle = function(candle, next) { + this.server.broadcastCandle(candle); + + next(); +}; + +Actor.prototype.processAdvice = function(advice) { + this.server.broadcastAdvice(advice); +}; + +module.exports = Actor; \ No newline at end of file diff --git a/plugins/xmppbot.js b/plugins/xmppbot.js new file mode 100644 index 0000000..cfca659 --- /dev/null +++ b/plugins/xmppbot.js @@ -0,0 +1,193 @@ +var log = require('../core/log'); +var moment = require('moment'); +var _ = require('lodash'); +var xmpp = require('node-xmpp-client'); +var config = require('../core/util').getConfig(); +var xmppbot = config.xmppbot; +var utc = moment.utc; + + + +var Actor = function() { + _.bindAll(this); + + this.bot = new xmpp.Client({ jid: xmppbot.client_id, + password: xmppbot.client_pwd, + host: xmppbot.client_host, + port: xmppbot.client_port + }); + + this.advice = 'Dont got one yet :('; + this.adviceTime = utc(); + this.state = xmppbot.status_msg; + this.price = 'Dont know yet :('; + this.priceTime = utc(); + + this.commands = { + ';;advice': 'emitAdvice', + ';;price': 'emitPrice', + ';;donate': 'emitDonation', + ';;real advice': 'emitRealAdvice', + ';;help': 'emitHelp' + }; + + this.rawCommands = _.keys(this.commands); + + this.bot.addListener('online', this.setState); + this.bot.addListener('stanza', this.rawStanza); + this.bot.addListener("error", this.logError); + this.bot.connection.socket.setTimeout(0) + this.bot.connection.socket.setKeepAlive(true, 10000) + +} + +Actor.prototype.setState = function() { + var elem = new xmpp.Element('presence', { }).c('show').t('chat').up().c('status').t(this.state) + this.bot.send(elem); +}; + +Actor.prototype.rawStanza = function(stanza) { + if (stanza.is('presence') && (stanza.attrs.type == 'subscribe')) { + this.bot.send(new xmpp.Element('presence', { to: stanza.attrs.from, type: 'subscribed' })); + } + if (stanza.is('message') && + // Important: never reply to errors! + stanza.attrs.type !== 'error') { + + // Swap addresses... + var from = stanza.attrs.from; + var body = stanza.getChild('body'); + if (!body) { + return; + } + + var message_recv = body.getText(); //Get Incoming Message + this.verifyQuestion(from, message_recv); + } +}; + +Actor.prototype.sendMessageTo = function(receiver, message){ + this.bot.send(new xmpp.Element('message', { to: receiver, type: 'chat' }). + c('body').t(message) + ); +}; +Actor.prototype.sendMessage = function(message) { + this.sendMessageTo(this.from, message); +}; + +Actor.prototype.processCandle = function(candle) { + this.price = candle.close; + this.priceTime = candle.start; +}; + +Actor.prototype.processAdvice = function(advice) { + if (xmppbot.muteSoft && advice.recommendation === 'soft') return; + this.advice = advice.recommendation; + this.adviceTime = utc(); + + if(xmppbot.emitUpdates) + this.newAdvice(xmppbot.receiver); +}; + +Actor.prototype.verifyQuestion = function(receiver, text) { + if(text in this.commands) + this[this.commands[text]](receiver); +} + +Actor.prototype.newAdvice = function(receiver) { + this.sendMessageTo(receiver, 'Important news!'); + this.emitAdvice(receiver); +} + +// sent advice +Actor.prototype.emitAdvice = function(receiver) { + var message = [ + 'Advice for ', + config.watch.exchange, + ' ', + config.watch.currency, + '/', + config.watch.asset, + ' using ', + config.tradingAdvisor.method, + ' at ', + config.tradingAdvisor.candleSize, + ' minute candles, is:\n', + this.advice, + ' ', + config.watch.asset, + ' (from ', + this.adviceTime.fromNow(), + ')' + ].join(''); + + this.sendMessageTo(receiver, message); +}; + +// sent price +Actor.prototype.emitPrice = function(receiver) { + + var message = [ + 'Current price at ', + config.watch.exchange, + ' ', + config.watch.currency, + '/', + config.watch.asset, + ' is ', + this.price, + ' ', + config.watch.currency, + ' (from ', + this.priceTime.fromNow(), + ')' + ].join(''); + + this.sendMessageTo(receiver, message); +}; + +// sent donation info +Actor.prototype.emitDonation = function(receiver) { + var message = 'You want to donate? How nice of you! You can send your coins here:'; + message += '\nBTC:\t19UGvmFPfFyFhPMHu61HTMGJqXRdVcHAj3'; + + this.sendMessageTo(receiver, message); +}; + +Actor.prototype.emitHelp = function(receiver) { + var message = _.reduce( + this.rawCommands, + function(message, command) { + return message + ' ' + command + ','; + }, + 'possible commands are:' + ); + + message = message.substr(0, _.size(message) - 1) + '.'; + + this.sendMessageTo(receiver, message); + +} + +Actor.prototype.emitRealAdvice = function(receiver) { + // http://www.examiner.com/article/uncaged-a-look-at-the-top-10-quotes-of-gordon-gekko + // http://elitedaily.com/money/memorable-gordon-gekko-quotes/ + var realAdvice = [ + 'I don\'t throw darts at a board. I bet on sure things. Read Sun-tzu, The Art of War. Every battle is won before it is ever fought.', + 'Ever wonder why fund managers can\'t beat the S&P 500? \'Cause they\'re sheep, and sheep get slaughtered.', + 'If you\'re not inside, you\'re outside!', + 'The most valuable commodity I know of is information.', + 'It\'s not a question of enough, pal. It\'s a zero sum game, somebody wins, somebody loses. Money itself isn\'t lost or made, it\'s simply transferred from one perception to another.', + 'What\'s worth doing is worth doing for money. (Wait, wasn\'t I a free and open source bot?)', + 'When I get a hold of the son of a bitch who leaked this, I\'m gonna tear his eyeballs out and I\'m gonna suck his fucking skull.' + ]; + + this.sendMessageTo(receiver, _.first(_.shuffle(realAdvice))); +} + +Actor.prototype.logError = function(message) { + log.error('XMPP ERROR:', message); +}; + + +module.exports = Actor; diff --git a/sample-config.js b/sample-config.js new file mode 100644 index 0000000..34c5092 --- /dev/null +++ b/sample-config.js @@ -0,0 +1,539 @@ +// Everything is explained here: +// @link https://gekko.wizb.it/docs/commandline/plugins.html + +var config = {}; + +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// GENERAL SETTINGS +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +config.debug = true; // for additional logging / debugging + +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// WATCHING A MARKET +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +config.watch = { + + // see https://gekko.wizb.it/docs/introduction/supported_exchanges.html + exchange: 'binance', + currency: 'USDT', + asset: 'BTC', +} + +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// CONFIGURING TRADING ADVICE +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +config.tradingAdvisor = { + enabled: true, + method: 'MACD', + candleSize: 60, + historySize: 10, +} + +// MACD settings: +config.MACD = { + // EMA weight (α) + // the higher the weight, the more smooth (and delayed) the line + short: 10, + long: 21, + signal: 9, + // the difference between the EMAs (to act as triggers) + thresholds: { + down: -0.025, + up: 0.025, + // How many candle intervals should a trend persist + // before we consider it real? + persistence: 1 + } +}; + +// settings for other strategies can be found at the bottom, note that only +// one strategy is active per gekko, the other settings are ignored. + +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// CONFIGURING PLUGINS +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +// do you want Gekko to simulate the profit of the strategy's own advice? +config.paperTrader = { + enabled: true, + // report the profit in the currency or the asset? + reportInCurrency: true, + // start balance, on what the current balance is compared with + simulationBalance: { + // these are in the unit types configured in the watcher. + asset: 1, + currency: 100, + }, + // how much fee in % does each trade cost? + feeMaker: 0.15, + feeTaker: 0.25, + feeUsing: 'maker', + // how much slippage/spread should Gekko assume per trade? + slippage: 0.05, +} + +config.performanceAnalyzer = { + enabled: true, + riskFreeReturn: 5 +} + +// Want Gekko to perform real trades on buy or sell advice? +// Enabling this will activate trades for the market being +// watched by `config.watch`. +config.trader = { + enabled: false, + key: '', + secret: '', + username: '', // your username, only required for specific exchanges. + passphrase: '', // GDAX, requires a passphrase. +} + +config.eventLogger = { + enabled: false, + // optionally pass a whitelist of events to log, if not past + // the eventLogger will log _all_ events. + // whitelist: ['portfolioChange', 'portfolioValueChange'] +} + +config.pushover = { + enabled: false, + sendPushoverOnStart: false, + muteSoft: true, // disable advice printout if it's soft + tag: '[GEKKO]', + key: '', + user: '' +} + +config.blotter = { + enabled: false, + filename: 'blotter.csv', + dateFormat: 'l LT', + timezone: -300, // -300 minutes for EST(-5:00), only used if exchange doesn't provide correct timezone +} + +// want Gekko to send a mail on buy or sell advice? +config.mailer = { + enabled: false, // Send Emails if true, false to turn off + sendMailOnStart: true, // Send 'Gekko starting' message if true, not if false + + email: '', // Your Gmail address + muteSoft: true, // disable advice printout if it's soft + + // You don't have to set your password here, if you leave it blank we will ask it + // when Gekko's starts. + // + // NOTE: Gekko is an open source project < https://github.com/askmike/gekko >, + // make sure you looked at the code or trust the maintainer of this bot when you + // fill in your email and password. + // + // WARNING: If you have NOT downloaded Gekko from the github page above we CANNOT + // guarantuee that your email address & password are safe! + + password: '', // Your Gmail Password - if not supplied Gekko will prompt on startup. + + tag: '[GEKKO] ', // Prefix all email subject lines with this + + // ADVANCED MAIL SETTINGS + // you can leave those as is if you + // just want to use Gmail + + server: 'smtp.gmail.com', // The name of YOUR outbound (SMTP) mail server. + smtpauth: true, // Does SMTP server require authentication (true for Gmail) + // The following 3 values default to the Email (above) if left blank + user: '', // Your Email server user name - usually your full Email address 'me@mydomain.com' + from: '', // 'me@mydomain.com' + to: '', // 'me@somedomain.com, me@someotherdomain.com' + ssl: true, // Use SSL (true for Gmail) + port: '', // Set if you don't want to use the default port +} + +config.pushbullet = { + // sends pushbullets if true + enabled: false, + // Send 'Gekko starting' message if true + sendMessageOnStart: true, + // Send Message for advice? Recommend Flase for paper, true for live + sendOnAdvice: true, + // Send Message on Trade Completion? + sendOnTrade: true, + // For Overall P/L calc. Pass in old balance if desired, else leave '0' + startingBalance: 0, + // your pushbullet API key + key: '', + // your email + email: 'jon_snow@westeros.com', + // Messages will start with this tag + tag: '[GEKKO]' +}; + +config.kodi = { + // if you have a username & pass, add it like below + // http://user:pass@ip-or-hostname:8080/jsonrpc + host: 'http://ip-or-hostname:8080/jsonrpc', + enabled: false, + sendMessageOnStart: true, +} + +config.ircbot = { + enabled: false, + emitUpdates: false, + muteSoft: true, + channel: '#your-channel', + server: 'irc.freenode.net', + botName: 'gekkobot' +} + +config.telegrambot = { + enabled: false, + // Receive notifications for trades and warnings/errors related to trading + emitTrades: false, + token: 'YOUR_TELEGRAM_BOT_TOKEN', +}; + +config.twitter = { + // sends pushbullets if true + enabled: false, + // Send 'Gekko starting' message if true + sendMessageOnStart: false, + // disable advice printout if it's soft + muteSoft: false, + tag: '[GEKKO]', + // twitter consumer key + consumer_key: '', + // twitter consumer secret + consumer_secret: '', + // twitter access token key + access_token_key: '', + // twitter access token secret + access_token_secret: '' +}; + +config.xmppbot = { + enabled: false, + emitUpdates: false, + client_id: 'jabber_id', + client_pwd: 'jabber_pw', + client_host: 'jabber_server', + client_port: 5222, + status_msg: 'I\'m online', + receiver: 'jabber_id_for_updates' +} + +config.campfire = { + enabled: false, + emitUpdates: false, + nickname: 'Gordon', + roomId: null, + apiKey: '', + account: '' +} + +config.redisBeacon = { + enabled: false, + port: 6379, // redis default + host: '127.0.0.1', // localhost + // On default Gekko broadcasts + // events in the channel with + // the name of the event, set + // an optional prefix to the + // channel name. + channelPrefix: '', + broadcast: [ + 'candle' + ] +} + +config.slack = { + enabled: false, + token: '', + sendMessageOnStart: true, + muteSoft: true, + channel: '' // #tradebot +} + +config.ifttt = { + enabled: false, + eventName: 'gekko', + makerKey: '', + muteSoft: true, + sendMessageOnStart: true +} + +config.candleWriter = { + enabled: true +} + +config.adviceWriter = { + enabled: false, + muteSoft: true, +} + +config.backtestResultExporter = { + enabled: false, + writeToDisk: false, + data: { + stratUpdates: false, + portfolioValues: true, + stratCandles: true, + roundtrips: true, + trades: true + } +} + +config.candleUploader = { + enabled: false, + url: '', + apiKey: '' +} + +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// CONFIGURING ADAPTER +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +config.adapter = 'sqlite'; + +config.sqlite = { + path: 'plugins/sqlite', + + dataDirectory: 'history', + version: 0.1, + + journalMode: require('./web/isWindows.js') ? 'DELETE' : 'WAL', + + dependencies: [] +} + +// Postgres adapter example config (please note: requires postgres >= 9.5): +config.postgresql = { + path: 'plugins/postgresql', + version: 0.1, + connectionString: 'postgres://user:pass@localhost:5432', // if default port + database: null, // if set, we'll put all tables into a single database. + schema: 'public', + dependencies: [{ + module: 'pg', + version: '7.4.3' + }] +} + +// Mongodb adapter, requires mongodb >= 3.3 (no version earlier tested) +config.mongodb = { + path: 'plugins/mongodb', + version: 0.1, + connectionString: 'mongodb://localhost/gekko', // connection to mongodb server + dependencies: [{ + module: 'mongojs', + version: '2.4.0' + }] +} + +config.candleUploader = { + enabled: false, + url: '', + apiKey: '' +} + +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// CONFIGURING BACKTESTING +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +// Note that these settings are only used in backtesting mode, see here: +// @link: https://gekko.wizb.it/docs/commandline/backtesting.html + +config.backtest = { + daterange: 'scan', + // daterange: { + // from: "2018-03-01", + // to: "2018-04-28" + //}, + batchSize: 50 +} + +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// CONFIGURING IMPORTING +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +config.importer = { + daterange: { + // NOTE: these dates are in UTC + from: "2017-11-01 00:00:00", + to: "2017-11-20 00:00:00" + } +} + + +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// OTHER STRATEGY SETTINGS +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +// Exponential Moving Averages settings: +config.DEMA = { + // EMA weight (α) + // the higher the weight, the more smooth (and delayed) the line + weight: 21, + // amount of candles to remember and base initial EMAs on + // the difference between the EMAs (to act as triggers) + thresholds: { + down: -0.025, + up: 0.025 + } +}; + +// PPO settings: +config.PPO = { + // EMA weight (α) + // the higher the weight, the more smooth (and delayed) the line + short: 12, + long: 26, + signal: 9, + // the difference between the EMAs (to act as triggers) + thresholds: { + down: -0.025, + up: 0.025, + // How many candle intervals should a trend persist + // before we consider it real? + persistence: 2 + } +}; + +// Uses one of the momentum indicators but adjusts the thresholds when PPO is bullish or bearish +// Uses settings from the ppo and momentum indicator config block +config.varPPO = { + momentum: 'TSI', // RSI, TSI or UO + thresholds: { + // new threshold is default threshold + PPOhist * PPOweight + weightLow: 120, + weightHigh: -120, + // How many candle intervals should a trend persist + // before we consider it real? + persistence: 0 + } +}; + +// RSI settings: +config.RSI = { + interval: 14, + thresholds: { + low: 30, + high: 70, + // How many candle intervals should a trend persist + // before we consider it real? + persistence: 1 + } +}; + +// TSI settings: +config.TSI = { + short: 13, + long: 25, + thresholds: { + low: -25, + high: 25, + // How many candle intervals should a trend persist + // before we consider it real? + persistence: 1 + } +}; + +// Ultimate Oscillator Settings +config.UO = { + first: { + weight: 4, + period: 7 + }, + second: { + weight: 2, + period: 14 + }, + third: { + weight: 1, + period: 28 + }, + thresholds: { + low: 30, + high: 70, + // How many candle intervals should a trend persist + // before we consider it real? + persistence: 1 + } +}; + +// CCI Settings +config.CCI = { + constant: 0.015, // constant multiplier. 0.015 gets to around 70% fit + history: 90, // history size, make same or smaller than history + thresholds: { + up: 100, // fixed values for overbuy upward trajectory + down: -100, // fixed value for downward trajectory + persistence: 0 // filter spikes by adding extra filters candles + } +}; + +// StochRSI settings +config.StochRSI = { + interval: 3, + thresholds: { + low: 20, + high: 80, + // How many candle intervals should a trend persist + // before we consider it real? + persistence: 3 + } +}; + + +// custom settings: +config.custom = { + my_custom_setting: 10, +} + +config['talib-macd'] = { + parameters: { + optInFastPeriod: 10, + optInSlowPeriod: 21, + optInSignalPeriod: 9 + }, + thresholds: { + down: -0.025, + up: 0.025, + } +} + +config['talib-macd'] = { + parameters: { + optInFastPeriod: 10, + optInSlowPeriod: 21, + optInSignalPeriod: 9 + }, + thresholds: { + down: -0.025, + up: 0.025, + } +} + +config['tulip-adx'] = { + optInTimePeriod: 10, + thresholds: { + down: -0.025, + up: 0.025, + } +} + + +// set this to true if you understand that Gekko will +// invest according to how you configured the indicators. +// None of the advice in the output is Gekko telling you +// to take a certain position. Instead it is the result +// of running the indicators you configured automatically. +// +// In other words: Gekko automates your trading strategies, +// it doesn't advice on itself, only set to true if you truly +// understand this. +// +// Not sure? Read this first: https://github.com/askmike/gekko/issues/201 +config['I understand that Gekko only automates MY OWN trading strategies'] = false; + +module.exports = config; \ No newline at end of file diff --git a/strategies/CCI.js b/strategies/CCI.js new file mode 100644 index 0000000..3cc8ccc --- /dev/null +++ b/strategies/CCI.js @@ -0,0 +1,125 @@ +// helpers +var _ = require('lodash'); +var log = require('../core/log.js'); + +// let's create our own method +var method = {}; + +// prepare everything our method needs +method.init = function() { + this.currentTrend; + this.requiredHistory = this.tradingAdvisor.historySize; + + this.age = 0; + this.trend = { + direction: 'undefined', + duration: 0, + persisted: false, + adviced: false + }; + this.historySize = this.settings.history; + this.ppoadv = 'none'; + this.uplevel = this.settings.thresholds.up; + this.downlevel = this.settings.thresholds.down; + this.persisted = this.settings.thresholds.persistence; + + // log.debug("CCI started with:\nup:\t", this.uplevel, "\ndown:\t", this.downlevel, "\npersistence:\t", this.persisted); + // define the indicators we need + this.addIndicator('cci', 'CCI', this.settings); +} + +// what happens on every new candle? +method.update = function(candle) { +} + +// for debugging purposes: log the last calculated +// EMAs and diff. +method.log = function(candle) { + var cci = this.indicators.cci; + if (typeof(cci.result) == 'boolean') { + log.debug('Insufficient data available. Age: ', cci.size, ' of ', cci.maxSize); + return; + } + + log.debug('calculated CCI properties for candle:'); + log.debug('\t', 'Price:\t\t', candle.close.toFixed(8)); + log.debug('\t', 'CCI tp:\t', cci.tp.toFixed(8)); + log.debug('\t', 'CCI tp/n:\t', cci.avgtp.toFixed(8)); + log.debug('\t', 'CCI md:\t', cci.mean.toFixed(8)); + if (typeof(cci.result) == 'boolean' ) + log.debug('\t In sufficient data available.'); + else + log.debug('\t', 'CCI:\t\t', cci.result.toFixed(2)); +} + +/* + * + */ +method.check = function(candle) { + + var lastPrice = candle.close; + + this.age++; + var cci = this.indicators.cci; + + if (typeof(cci.result) == 'number') { + + // overbought? + if (cci.result >= this.uplevel && (this.trend.persisted || this.persisted == 0) && !this.trend.adviced && this.trend.direction == 'overbought' ) { + this.trend.adviced = true; + this.trend.duration++; + this.advice('short'); + } else if (cci.result >= this.uplevel && this.trend.direction != 'overbought') { + this.trend.duration = 1; + this.trend.direction = 'overbought'; + this.trend.persisted = false; + this.trend.adviced = false; + if (this.persisted == 0) { + this.trend.adviced = true; + this.advice('short'); + } + } else if (cci.result >= this.uplevel) { + this.trend.duration++; + if (this.trend.duration >= this.persisted) { + this.trend.persisted = true; + } + } else if (cci.result <= this.downlevel && (this.trend.persisted || this.persisted == 0) && !this.trend.adviced && this.trend.direction == 'oversold') { + this.trend.adviced = true; + this.trend.duration++; + this.advice('long'); + } else if (cci.result <= this.downlevel && this.trend.direction != 'oversold') { + this.trend.duration = 1; + this.trend.direction = 'oversold'; + this.trend.persisted = false; + this.trend.adviced = false; + if (this.persisted == 0) { + this.trend.adviced = true; + this.advice('long'); + } + } else if (cci.result <= this.downlevel) { + this.trend.duration++; + if (this.trend.duration >= this.persisted) { + this.trend.persisted = true; + } + } else { + if( this.trend.direction != 'nodirection') { + this.trend = { + direction: 'nodirection', + duration: 0, + persisted: false, + adviced: false + }; + } else { + this.trend.duration++; + } + this.advice(); + } + + } else { + this.advice(); + } + + log.debug("Trend: ", this.trend.direction, " for ", this.trend.duration); +} + +module.exports = method; diff --git a/strategies/DEBUG_single-advice.js b/strategies/DEBUG_single-advice.js new file mode 100644 index 0000000..fbc6eb2 --- /dev/null +++ b/strategies/DEBUG_single-advice.js @@ -0,0 +1,37 @@ +var settings = { + wait: 0, + // advice: 'short' + advice: 'long' +}; + +// ------- + +var _ = require('lodash'); +var log = require('../core/log.js'); + +var i = 0; + +var method = { + init: _.noop, + update: _.noop, + log: _.noop, + check: function() { + + // log.info('iteration:', i); + if(settings.wait === i) { + console.log('trigger advice!'); + this.advice({ + direction: settings.advice, + trigger: { + type: 'trailingStop', + trailPercentage: 0.5 + } + }); + } + + i++ + + } +}; + +module.exports = method; \ No newline at end of file diff --git a/strategies/DEBUG_toggle-advice.js b/strategies/DEBUG_toggle-advice.js new file mode 100644 index 0000000..251585a --- /dev/null +++ b/strategies/DEBUG_toggle-advice.js @@ -0,0 +1,45 @@ +var settings = { + wait: 0, + each: 6 +}; + +// ------- + +var _ = require('lodash'); +var log = require('../core/log.js'); + +var i = 0; + +var method = { + init: _.noop, + update: _.noop, + log: _.noop, + processTrade: function(trade) { + log.debug('TRADE RECEIVED BY processTrade:', trade); + }, + check: function(candle) { + + if(settings.wait > i) + return; + + log.info('iteration:', i); + + if(i % settings.each === 0) { + log.debug('trigger SHORT'); + this.advice('short'); + } else if(i % settings.each === settings.each / 2) { + log.debug('trigger LONG'); + this.advice('long'); + } + + // if(i % 2 === 0) + // this.advice('long'); + // else if(i % 2 === 1) + // this.advice('short'); + + i++; + + } +}; + +module.exports = method; diff --git a/strategies/DEMA.js b/strategies/DEMA.js new file mode 100644 index 0000000..83ab0f7 --- /dev/null +++ b/strategies/DEMA.js @@ -0,0 +1,73 @@ +// helpers +var _ = require('lodash'); +var log = require('../core/log.js'); + +// let's create our own method +var method = {}; + +// prepare everything our method needs +method.init = function() { + this.name = 'DEMA'; + + this.currentTrend; + this.requiredHistory = this.tradingAdvisor.historySize; + + // define the indicators we need + this.addIndicator('dema', 'DEMA', this.settings); + this.addIndicator('sma', 'SMA', this.settings.weight); +} + +// what happens on every new candle? +method.update = function(candle) { + // nothing! +} + +// for debugging purposes: log the last calculated +// EMAs and diff. +method.log = function() { + let dema = this.indicators.dema; + let sma = this.indicators.sma; + + log.debug('Calculated DEMA and SMA properties for candle:'); + log.debug('\t Inner EMA:', dema.inner.result.toFixed(8)); + log.debug('\t Outer EMA:', dema.outer.result.toFixed(8)); + log.debug('\t DEMA:', dema.result.toFixed(5)); + log.debug('\t SMA:', sma.result.toFixed(5)); + log.debug('\t DEMA age:', dema.inner.age, 'candles'); +} + +method.check = function(candle) { + let dema = this.indicators.dema; + let sma = this.indicators.sma; + let resDEMA = dema.result; + let resSMA = sma.result; + let price = candle.close; + let diff = resSMA - resDEMA; + + let message = '@ ' + price.toFixed(8) + ' (' + resDEMA.toFixed(5) + '/' + diff.toFixed(5) + ')'; + + if(diff > this.settings.thresholds.up) { + log.debug('we are currently in uptrend', message); + + if(this.currentTrend !== 'up') { + this.currentTrend = 'up'; + this.advice('long'); + } else + this.advice(); + + } else if(diff < this.settings.thresholds.down) { + log.debug('we are currently in a downtrend', message); + + if(this.currentTrend !== 'down') { + this.currentTrend = 'down'; + this.advice('short'); + } else + this.advice(); + + } else { + log.debug('we are currently not in an up or down trend', message); + this.advice(); + } +} + +module.exports = method; diff --git a/strategies/MACD.js b/strategies/MACD.js new file mode 100644 index 0000000..509e418 --- /dev/null +++ b/strategies/MACD.js @@ -0,0 +1,134 @@ +/* + + MACD - DJM 31/12/2013 + + (updated a couple of times since, check git history) + + */ + +// helpers +var _ = require('lodash'); +var log = require('../core/log.js'); + +// let's create our own method +var method = {}; + +// prepare everything our method needs +method.init = function() { + // keep state about the current trend + // here, on every new candle we use this + // state object to check if we need to + // report it. + this.trend = { + direction: 'none', + duration: 0, + persisted: false, + adviced: false + }; + + // how many candles do we need as a base + // before we can start giving advice? + this.requiredHistory = this.tradingAdvisor.historySize; + + // define the indicators we need + this.addIndicator('macd', 'MACD', this.settings); +} + +// what happens on every new candle? +method.update = function(candle) { + // nothing! +} + +// for debugging purposes: log the last calculated +// EMAs and diff. +method.log = function() { + var digits = 8; + var macd = this.indicators.macd; + + var diff = macd.diff; + var signal = macd.signal.result; + + log.debug('calculated MACD properties for candle:'); + log.debug('\t', 'short:', macd.short.result.toFixed(digits)); + log.debug('\t', 'long:', macd.long.result.toFixed(digits)); + log.debug('\t', 'macd:', diff.toFixed(digits)); + log.debug('\t', 'signal:', signal.toFixed(digits)); + log.debug('\t', 'macdiff:', macd.result.toFixed(digits)); +} + +method.check = function() { + var macddiff = this.indicators.macd.result; + + if(macddiff > this.settings.thresholds.up) { + + // new trend detected + if(this.trend.direction !== 'up') + // reset the state for the new trend + this.trend = { + duration: 0, + persisted: false, + direction: 'up', + adviced: false + }; + + this.trend.duration++; + + log.debug('In uptrend since', this.trend.duration, 'candle(s)'); + + if(this.trend.duration >= this.settings.thresholds.persistence) + this.trend.persisted = true; + + if(this.trend.persisted && !this.trend.adviced) { + this.trend.adviced = true; + this.advice('long'); + } else + this.advice(); + + } else if(macddiff < this.settings.thresholds.down) { + + // new trend detected + if(this.trend.direction !== 'down') + // reset the state for the new trend + this.trend = { + duration: 0, + persisted: false, + direction: 'down', + adviced: false + }; + + this.trend.duration++; + + log.debug('In downtrend since', this.trend.duration, 'candle(s)'); + + if(this.trend.duration >= this.settings.thresholds.persistence) + this.trend.persisted = true; + + if(this.trend.persisted && !this.trend.adviced) { + this.trend.adviced = true; + this.advice('short'); + } else + this.advice(); + + } else { + + log.debug('In no trend'); + + // we're not in an up nor in a downtrend + // but for now we ignore sideways trends + // + // read more @link: + // + // https://github.com/askmike/gekko/issues/171 + + // this.trend = { + // direction: 'none', + // duration: 0, + // persisted: false, + // adviced: false + // }; + + this.advice(); + } +} + +module.exports = method; diff --git a/strategies/PPO.js b/strategies/PPO.js new file mode 100644 index 0000000..8c0f214 --- /dev/null +++ b/strategies/PPO.js @@ -0,0 +1,148 @@ +/* + + PPO - cykedev 15/01/2014 + + (updated a couple of times since, check git history) + + */ + +// helpers +var _ = require('lodash'); +var log = require('../core/log'); + +// let's create our own method +var method = {}; + +// prepare everything our method needs +method.init = function() { + this.name = 'PPO'; + + this.trend = { + direction: 'none', + duration: 0, + persisted: false, + adviced: false + }; + + this.requiredHistory = this.tradingAdvisor.historySize; + + // define the indicators we need + this.addIndicator('ppo', 'PPO', this.settings); +} + +// what happens on every new candle? +method.update = function(candle) { + // nothing! +} + +// for debugging purposes log the last +// calculated parameters. +method.log = function() { + var digits = 8; + var ppo = this.indicators.ppo; + var long = ppo.result.longEMA; + var short = ppo.result.shortEMA; + var macd = ppo.result.macd; + var result = ppo.result.ppo; + var macdSignal = ppo.result.MACDsignal; + var ppoSignal = ppo.result.PPOsignal; + + log.debug('calculated MACD properties for candle:'); + log.debug('\t', 'short:', short.toFixed(digits)); + log.debug('\t', 'long:', long.toFixed(digits)); + log.debug('\t', 'macd:', macd.toFixed(digits)); + log.debug('\t', 'macdsignal:', macdSignal.toFixed(digits)); + log.debug('\t', 'machist:', (macd - macdSignal).toFixed(digits)); + log.debug('\t', 'ppo:', result.toFixed(digits)); + log.debug('\t', 'pposignal:', ppoSignal.toFixed(digits)); + log.debug('\t', 'ppohist:', (result - ppoSignal).toFixed(digits)); +} + +method.check = function(candle) { + var price = candle.close; + + var ppo = this.indicators.ppo; + var long = ppo.result.longEMA; + var short = ppo.result.shortEMA; + var macd = ppo.result.macd; + var result = ppo.result.ppo; + var macdSignal = ppo.result.MACDsignal; + var ppoSignal = ppo.result.PPOsignal; + + // TODO: is this part of the indicator or not? + // if it is it should move there + var ppoHist = result - ppoSignal; + + if(ppoHist > this.settings.thresholds.up) { + + // new trend detected + if(this.trend.direction !== 'up') + this.trend = { + duration: 0, + persisted: false, + direction: 'up', + adviced: false + }; + + this.trend.duration++; + + log.debug('In uptrend since', this.trend.duration, 'candle(s)'); + + if(this.trend.duration >= this.settings.thresholds.persistence) + this.trend.persisted = true; + + if(this.trend.persisted && !this.trend.adviced) { + this.trend.adviced = true; + this.advice('long'); + } else + this.advice(); + + } else if(ppoHist < this.settings.thresholds.down) { + + // new trend detected + if(this.trend.direction !== 'down') + this.trend = { + duration: 0, + persisted: false, + direction: 'down', + adviced: false + }; + + this.trend.duration++; + + log.debug('In downtrend since', this.trend.duration, 'candle(s)'); + + if(this.trend.duration >= this.settings.thresholds.persistence) + this.trend.persisted = true; + + if(this.trend.persisted && !this.trend.adviced) { + this.trend.adviced = true; + this.advice('short'); + } else + this.advice(); + + + } else { + + log.debug('In no trend'); + + // we're not in an up nor in a downtrend + // but for now we ignore sideways trends + // + // read more @link: + // + // https://github.com/askmike/gekko/issues/171 + + // this.trend = { + // direction: 'none', + // duration: 0, + // persisted: false, + // adviced: false + // }; + + this.advice(); + } + +} + +module.exports = method; diff --git a/strategies/RSI.js b/strategies/RSI.js new file mode 100644 index 0000000..39145de --- /dev/null +++ b/strategies/RSI.js @@ -0,0 +1,105 @@ +/* + + RSI - cykedev 14/02/2014 + + (updated a couple of times since, check git history) + + */ +// helpers +var _ = require('lodash'); +var log = require('../core/log.js'); + +var RSI = require('./indicators/RSI.js'); + +// let's create our own method +var method = {}; + +// prepare everything our method needs +method.init = function() { + this.name = 'RSI'; + + this.trend = { + direction: 'none', + duration: 0, + persisted: false, + adviced: false + }; + + this.requiredHistory = this.tradingAdvisor.historySize; + + // define the indicators we need + this.addIndicator('rsi', 'RSI', this.settings); +} + +// for debugging purposes log the last +// calculated parameters. +method.log = function(candle) { + var digits = 8; + var rsi = this.indicators.rsi; + + log.debug('calculated RSI properties for candle:'); + log.debug('\t', 'rsi:', rsi.result.toFixed(digits)); + log.debug('\t', 'price:', candle.close.toFixed(digits)); +} + +method.check = function() { + var rsi = this.indicators.rsi; + var rsiVal = rsi.result; + + if(rsiVal > this.settings.thresholds.high) { + + // new trend detected + if(this.trend.direction !== 'high') + this.trend = { + duration: 0, + persisted: false, + direction: 'high', + adviced: false + }; + + this.trend.duration++; + + log.debug('In high since', this.trend.duration, 'candle(s)'); + + if(this.trend.duration >= this.settings.thresholds.persistence) + this.trend.persisted = true; + + if(this.trend.persisted && !this.trend.adviced) { + this.trend.adviced = true; + this.advice('short'); + } else + this.advice(); + + } else if(rsiVal < this.settings.thresholds.low) { + + // new trend detected + if(this.trend.direction !== 'low') + this.trend = { + duration: 0, + persisted: false, + direction: 'low', + adviced: false + }; + + this.trend.duration++; + + log.debug('In low since', this.trend.duration, 'candle(s)'); + + if(this.trend.duration >= this.settings.thresholds.persistence) + this.trend.persisted = true; + + if(this.trend.persisted && !this.trend.adviced) { + this.trend.adviced = true; + this.advice('long'); + } else + this.advice(); + + } else { + + log.debug('In no trend'); + + this.advice(); + } +} + +module.exports = method; diff --git a/strategies/StochRSI.js b/strategies/StochRSI.js new file mode 100644 index 0000000..72b93ae --- /dev/null +++ b/strategies/StochRSI.js @@ -0,0 +1,121 @@ +/* + + StochRSI - SamThomp 11/06/2014 + + (updated by askmike) @ 30/07/2016 + + */ +// helpers +var _ = require('lodash'); +var log = require('../core/log.js'); + +var RSI = require('./indicators/RSI.js'); + +// let's create our own method +var method = {}; + +// prepare everything our method needs +method.init = function() { + this.interval = this.settings.interval; + + this.trend = { + direction: 'none', + duration: 0, + persisted: false, + adviced: false + }; + + this.requiredHistory = this.tradingAdvisor.historySize; + + // define the indicators we need + this.addIndicator('rsi', 'RSI', { interval: this.interval }); + + this.RSIhistory = []; +} + +// what happens on every new candle? +method.update = function(candle) { + this.rsi = this.indicators.rsi.result; + + this.RSIhistory.push(this.rsi); + + if(_.size(this.RSIhistory) > this.interval) + // remove oldest RSI value + this.RSIhistory.shift(); + + this.lowestRSI = _.min(this.RSIhistory); + this.highestRSI = _.max(this.RSIhistory); + this.stochRSI = ((this.rsi - this.lowestRSI) / (this.highestRSI - this.lowestRSI)) * 100; +} + +// for debugging purposes log the last +// calculated parameters. +method.log = function() { + var digits = 8; + + log.debug('calculated StochRSI properties for candle:'); + log.debug('\t', 'rsi:', this.rsi.toFixed(digits)); + log.debug("StochRSI min:\t\t" + this.lowestRSI.toFixed(digits)); + log.debug("StochRSI max:\t\t" + this.highestRSI.toFixed(digits)); + log.debug("StochRSI Value:\t\t" + this.stochRSI.toFixed(2)); +} + +method.check = function() { + if(this.stochRSI > this.settings.thresholds.high) { + // new trend detected + if(this.trend.direction !== 'high') + this.trend = { + duration: 0, + persisted: false, + direction: 'high', + adviced: false + }; + + this.trend.duration++; + + log.debug('In high since', this.trend.duration, 'candle(s)'); + + if(this.trend.duration >= this.settings.thresholds.persistence) + this.trend.persisted = true; + + if(this.trend.persisted && !this.trend.adviced) { + this.trend.adviced = true; + this.advice('short'); + } else + this.advice(); + + } else if(this.stochRSI < this.settings.thresholds.low) { + + // new trend detected + if(this.trend.direction !== 'low') + this.trend = { + duration: 0, + persisted: false, + direction: 'low', + adviced: false + }; + + this.trend.duration++; + + log.debug('In low since', this.trend.duration, 'candle(s)'); + + if(this.trend.duration >= this.settings.thresholds.persistence) + this.trend.persisted = true; + + if(this.trend.persisted && !this.trend.adviced) { + this.trend.adviced = true; + this.advice('long'); + } else + this.advice(); + + } else { + // trends must be on consecutive candles + this.trend.duration = 0; + log.debug('In no trend'); + + this.advice(); + } + +} + +module.exports = method; diff --git a/strategies/TMA.js b/strategies/TMA.js new file mode 100644 index 0000000..7292153 --- /dev/null +++ b/strategies/TMA.js @@ -0,0 +1,34 @@ +var method = {}; + +method.init = function() { + this.name = 'Triple Moving Average'; + this.requiredHistory = this.settings.long; + + this.addIndicator('short', 'SMA', this.settings.short) + this.addIndicator('medium', 'SMA', this.settings.medium) + this.addIndicator('long', 'SMA', this.settings.long) +} + +method.update = function(candle) { + this.indicators.short.update(candle.close) + this.indicators.medium.update(candle.close) + this.indicators.long.update(candle.close) +} + +method.check = function() { + const short = this.indicators.short.result; + const medium = this.indicators.medium.result; + const long = this.indicators.long.result; + + if((short > medium) && (medium > long)) { + this.advice('long') + } else if((short < medium) && (medium > long)) { + this.advice('short') + } else if(((short > medium) && (medium < long))) { + this.advice('short') + } else { + this.advice(); + } +} + +module.exports = method; diff --git a/strategies/TSI.js b/strategies/TSI.js new file mode 100644 index 0000000..7f4cd64 --- /dev/null +++ b/strategies/TSI.js @@ -0,0 +1,98 @@ +// helpers +var _ = require('lodash'); +var log = require('../core/log.js'); + +var TSI = require('./indicators/TSI.js'); + +// let's create our own method +var method = {}; + +// prepare everything our method needs +method.init = function() { + this.name = 'TSI'; + + this.trend = { + direction: 'none', + duration: 0, + persisted: false, + adviced: false + }; + + this.requiredHistory = this.tradingAdvisor.historySize; + + // define the indicators we need + this.addIndicator('tsi', 'TSI', this.settings); +} + +// for debugging purposes log the last +// calculated parameters. +method.log = function(candle) { + var digits = 8; + var tsi = this.indicators.tsi; + + log.debug('calculated TSI properties for candle:'); + log.debug('\t', 'tsi:', tsi.tsi.toFixed(digits)); + log.debug('\t', 'price:', candle.close.toFixed(digits)); +} + +method.check = function() { + var tsi = this.indicators.tsi; + var tsiVal = tsi.tsi; + + if(tsiVal > this.settings.thresholds.high) { + + // new trend detected + if(this.trend.direction !== 'high') + this.trend = { + duration: 0, + persisted: false, + direction: 'high', + adviced: false + }; + + this.trend.duration++; + + log.debug('In high since', this.trend.duration, 'candle(s)'); + + if(this.trend.duration >= this.settings.thresholds.persistence) + this.trend.persisted = true; + + if(this.trend.persisted && !this.trend.adviced) { + this.trend.adviced = true; + this.advice('short'); + } else + this.advice(); + + } else if(tsiVal < this.settings.thresholds.low) { + + // new trend detected + if(this.trend.direction !== 'low') + this.trend = { + duration: 0, + persisted: false, + direction: 'low', + adviced: false + }; + + this.trend.duration++; + + log.debug('In low since', this.trend.duration, 'candle(s)'); + + if(this.trend.duration >= this.settings.thresholds.persistence) + this.trend.persisted = true; + + if(this.trend.persisted && !this.trend.adviced) { + this.trend.adviced = true; + this.advice('long'); + } else + this.advice(); + + } else { + + log.debug('In no trend'); + + this.advice(); + } +} + +module.exports = method; diff --git a/strategies/UO.js b/strategies/UO.js new file mode 100644 index 0000000..b1efdef --- /dev/null +++ b/strategies/UO.js @@ -0,0 +1,98 @@ +// helpers +var _ = require('lodash'); +var log = require('../core/log.js'); + +var UO = require('./indicators/UO.js'); + +// let's create our own method +var method = {}; + +// prepare everything our method needs +method.init = function() { + this.name = 'UO'; + + this.trend = { + direction: 'none', + duration: 0, + persisted: false, + adviced: false + }; + + this.requiredHistory = this.tradingAdvisor.historySize; + + // define the indicators we need + this.addIndicator('uo', 'UO', this.settings); +} + +// for debugging purposes log the last +// calculated parameters. +method.log = function(candle) { + var digits = 8; + var uo = this.indicators.uo; + + log.debug('calculated Ultimate Oscillator properties for candle:'); + log.debug('\t', 'UO:', uo.uo.toFixed(digits)); + log.debug('\t', 'price:', candle.close.toFixed(digits)); +} + +method.check = function() { + var uo = this.indicators.uo; + var uoVal = uo.uo; + + if(uoVal > this.settings.thresholds.high) { + + // new trend detected + if(this.trend.direction !== 'high') + this.trend = { + duration: 0, + persisted: false, + direction: 'high', + adviced: false + }; + + this.trend.duration++; + + log.debug('In high since', this.trend.duration, 'candle(s)'); + + if(this.trend.duration >= this.settings.thresholds.persistence) + this.trend.persisted = true; + + if(this.trend.persisted && !this.trend.adviced) { + this.trend.adviced = true; + this.advice('short'); + } else + this.advice(); + + } else if(uoVal < this.settings.thresholds.low) { + + // new trend detected + if(this.trend.direction !== 'low') + this.trend = { + duration: 0, + persisted: false, + direction: 'low', + adviced: false + }; + + this.trend.duration++; + + log.debug('In low since', this.trend.duration, 'candle(s)'); + + if(this.trend.duration >= this.settings.thresholds.persistence) + this.trend.persisted = true; + + if(this.trend.persisted && !this.trend.adviced) { + this.trend.adviced = true; + this.advice('long'); + } else + this.advice(); + + } else { + + log.debug('In no trend'); + + this.advice(); + } +} + +module.exports = method; diff --git a/strategies/custom.js b/strategies/custom.js new file mode 100644 index 0000000..252cbbf --- /dev/null +++ b/strategies/custom.js @@ -0,0 +1,63 @@ +// This is a basic example strategy for Gekko. +// For more information on everything please refer +// to this document: +// +// https://gekko.wizb.it/docs/strategies/creating_a_strategy.html +// +// The example below is pretty bad investment advice: on every new candle there is +// a 10% chance it will recommend to change your position (to either +// long or short). + +var log = require('../core/log'); + +// Let's create our own strat +var strat = {}; + +// Prepare everything our method needs +strat.init = function() { + this.input = 'candle'; + this.currentTrend = 'long'; + this.requiredHistory = 0; +} + +// What happens on every new candle? +strat.update = function(candle) { + + // Get a random number between 0 and 1. + this.randomNumber = Math.random(); + + // There is a 10% chance it is smaller than 0.1 + this.toUpdate = this.randomNumber < 0.1; +} + +// For debugging purposes. +strat.log = function() { + log.debug('calculated random number:'); + log.debug('\t', this.randomNumber.toFixed(3)); +} + +// Based on the newly calculated +// information, check if we should +// update or not. +strat.check = function() { + + // Only continue if we have a new update. + if(!this.toUpdate) + return; + + if(this.currentTrend === 'long') { + + // If it was long, set it to short + this.currentTrend = 'short'; + this.advice('short'); + + } else { + + // If it was short, set it to long + this.currentTrend = 'long'; + this.advice('long'); + + } +} + +module.exports = strat; diff --git a/strategies/indicators/CCI.js b/strategies/indicators/CCI.js new file mode 100644 index 0000000..bca4817 --- /dev/null +++ b/strategies/indicators/CCI.js @@ -0,0 +1,75 @@ +/* + * CCI + */ +var log = require('../../core/log'); + +var Indicator = function(settings) { + this.input = 'candle'; + this.tp = 0.0; + this.result = false; + this.hist = []; // needed for mean? + this.mean = 0.0; + this.size = 0; + this.constant = settings.constant; + this.maxSize = settings.history; + for (var i = 0; i < this.maxSize; i++) + this.hist.push(0.0); +} + +Indicator.prototype.update = function(candle) { + + // We need sufficient history to get the right result. + + var tp = (candle.high + candle.close + candle.low) / 3; + if (this.size < this.maxSize) { + this.hist[this.size] = tp; + this.size++; + } else { + for (var i = 0; i < this.maxSize-1; i++) { + this.hist[i] = this.hist[i+1]; + } + this.hist[this.maxSize-1] = tp; + } + + if (this.size < this.maxSize) { + this.result = false; + } else { + this.calculate(tp); + } +} + +/* + * Handle calculations + */ +Indicator.prototype.calculate = function(tp) { + + var sumtp = 0.0 + + for (var i = 0; i < this.size; i++) { + sumtp = sumtp + this.hist[i]; + } + + this.avgtp = sumtp / this.size; + + this.tp = tp; + + var sum = 0.0; + // calculate tps + for (var i = 0; i < this.size; i++) { + + var z = (this.hist[i] - this.avgtp); + if (z < 0) z = z * -1.0; + sum = sum + z; + + } + + this.mean = (sum / this.size); + + + + this.result = (this.tp - this.avgtp) / (this.constant * this.mean); + + // log.debug("===\t", this.mean, "\t", this.tp, '\t', this.TP.result, "\t", sum, "\t", avgtp, '\t', this.result.toFixed(2)); +} + +module.exports = Indicator; diff --git a/strategies/indicators/DEMA.js b/strategies/indicators/DEMA.js new file mode 100644 index 0000000..316964b --- /dev/null +++ b/strategies/indicators/DEMA.js @@ -0,0 +1,19 @@ +// required indicators +var EMA = require('./EMA.js'); + +var Indicator = function(config) { + this.input = 'price'; + this.result = false; + this.inner = new EMA(config.weight); + this.outer = new EMA(config.weight); +} + +// add a price and calculate the EMAs and +// the result +Indicator.prototype.update = function(price) { + this.inner.update(price); + this.outer.update(this.inner.result); + this.result = 2 * this.inner.result - this.outer.result; +} + +module.exports = Indicator; diff --git a/strategies/indicators/EMA.js b/strategies/indicators/EMA.js new file mode 100644 index 0000000..8e398fc --- /dev/null +++ b/strategies/indicators/EMA.js @@ -0,0 +1,36 @@ +// @link http://en.wikipedia.org/wiki/Exponential_moving_average#Exponential_moving_average + +var Indicator = function(weight) { + this.input = 'price'; + this.weight = weight; + this.result = false; + this.age = 0; +} + +Indicator.prototype.update = function(price) { + // The first time we can't calculate based on previous + // ema, because we haven't calculated any yet. + if(this.result === false) + this.result = price; + + this.age++; + this.calculate(price); + + return this.result; +} + +// calculation (based on tick/day): +// EMA = Price(t) * k + EMA(y) * (1 – k) +// t = today, y = yesterday, N = number of days in EMA, k = 2 / (N+1) +Indicator.prototype.calculate = function(price) { + // weight factor + var k = 2 / (this.weight + 1); + + // yesterday + var y = this.result; + + // calculation + this.result = price * k + y * (1 - k); +} + +module.exports = Indicator; diff --git a/strategies/indicators/LRC.js b/strategies/indicators/LRC.js new file mode 100644 index 0000000..5f3a299 --- /dev/null +++ b/strategies/indicators/LRC.js @@ -0,0 +1,115 @@ +/* + * Linear regression curve + */ +var log = require('../../core/log'); + +var Indicator = function(settings) { + this.input = 'price'; + this.depth = settings; + this.result = false; + this.age = 0; + this.history = []; + this.x = []; + /* + * Do not use array(depth) as it might not be implemented + */ + for (var i = 0; i < this.depth; i++) { + this.history.push(0.0); + this.x.push(i); + } + + // log.debug("Created LRC indicator with h: ", this.depth); +} + +Indicator.prototype.update = function(price) { + + // We need sufficient history to get the right result. + if(this.result === false && this.age < this.depth) { + + this.history[this.age] = price; + this.age++; + this.result = false; + // log.debug("Waiting for sufficient age: ", this.age, " out of ", this.depth); + // + return; + } + + this.age++; + // shift history + for (var i = 0; i < (this.depth - 1); i++) { + this.history[i] = this.history[i+1]; + } + this.history[this.depth-1] = price; + + this.calculate(price); + + + // log.debug("Checking LRC: ", this.result.toFixed(8), "\tH: ", this.age); + return; +} + +/* + * Least squares linear regression fitting. + */ +function linreg(values_x, values_y) { + var sum_x = 0; + var sum_y = 0; + var sum_xy = 0; + var sum_xx = 0; + var count = 0; + + /* + * We'll use those variables for faster read/write access. + */ + var x = 0; + var y = 0; + var values_length = values_x.length; + + if (values_length != values_y.length) { + throw new Error('The parameters values_x and values_y need to have same size!'); + } + + /* + * Nothing to do. + */ + if (values_length === 0) { + return [ [], [] ]; + } + + /* + * Calculate the sum for each of the parts necessary. + */ + for (var v = 0; v < values_length; v++) { + x = values_x[v]; + y = values_y[v]; + sum_x += x; + sum_y += y; + sum_xx += x*x; + sum_xy += x*y; + count++; + } + + /* + * Calculate m and b for the formular: + * y = x * m + b + */ + var m = (count*sum_xy - sum_x*sum_y) / (count*sum_xx - sum_x*sum_x); + var b = (sum_y/count) - (m*sum_x)/count; + + return [m, b]; +} + + +/* + * Handle calculations + */ +Indicator.prototype.calculate = function(price) { + + // get the reg + var reg = linreg(this.x, this.history); + + // y = a * x + b + this.result = ((this.depth-1) * reg[0]) + reg[1]; +} + +module.exports = Indicator; diff --git a/strategies/indicators/MACD.js b/strategies/indicators/MACD.js new file mode 100644 index 0000000..5e400c4 --- /dev/null +++ b/strategies/indicators/MACD.js @@ -0,0 +1,27 @@ +// required indicators +var EMA = require('./EMA.js'); + +var Indicator = function(config) { + this.input = 'price'; + this.diff = false; + this.short = new EMA(config.short); + this.long = new EMA(config.long); + this.signal = new EMA(config.signal); +} + +Indicator.prototype.update = function(price) { + this.short.update(price); + this.long.update(price); + this.calculateEMAdiff(); + this.signal.update(this.diff); + this.result = this.diff - this.signal.result; +} + +Indicator.prototype.calculateEMAdiff = function() { + var shortEMA = this.short.result; + var longEMA = this.long.result; + + this.diff = shortEMA - longEMA; +} + +module.exports = Indicator; diff --git a/strategies/indicators/PPO.js b/strategies/indicators/PPO.js new file mode 100644 index 0000000..b49e592 --- /dev/null +++ b/strategies/indicators/PPO.js @@ -0,0 +1,37 @@ +// required indicators +var EMA = require('./EMA.js'); + +var Indicator = function(config) { + this.result = {}; + this.input = 'price'; + this.macd = 0; + this.ppo = 0; + this.short = new EMA(config.short); + this.long = new EMA(config.long); + this.MACDsignal = new EMA(config.signal); + this.PPOsignal = new EMA(config.signal); +} + +Indicator.prototype.update = function(price) { + this.short.update(price); + this.long.update(price); + this.calculatePPO(); + this.MACDsignal.update(this.result.macd); + this.MACDhist = this.result.macd - this.MACDsignal.result; + this.PPOsignal.update(this.result.ppo); + this.PPOhist = this.result.ppo - this.PPOsignal.result; + + this.result.MACDsignal = this.MACDsignal.result; + this.result.MACDhist = this.MACDhist; + this.result.PPOsignal = this.PPOsignal.result; + this.result.PPOhist = this.PPOhist; +} + +Indicator.prototype.calculatePPO = function() { + this.result.shortEMA = this.short.result; + this.result.longEMA = this.long.result; + this.result.macd = this.result.shortEMA - this.result.longEMA; + this.result.ppo = 100 * (this.result.macd / this.result.longEMA); +} + +module.exports = Indicator; diff --git a/strategies/indicators/RSI.js b/strategies/indicators/RSI.js new file mode 100644 index 0000000..03186dd --- /dev/null +++ b/strategies/indicators/RSI.js @@ -0,0 +1,53 @@ +// required indicators +var SMMA = require('./SMMA.js'); + +var Indicator = function (settings) { + this.input = 'candle'; + this.lastClose = null; + this.weight = settings.interval; + this.avgU = new SMMA(this.weight); + this.avgD = new SMMA(this.weight); + this.u = 0; + this.d = 0; + this.rs = 0; + this.result = 0; + this.age = 0; +} + +Indicator.prototype.update = function (candle) { + var currentClose = candle.close; + + if (this.lastClose === null) { + // Set initial price to prevent invalid change calculation + this.lastClose = currentClose; + + // Do not calculate RSI for this reason - there's no change! + this.age++; + return; + } + + if (currentClose > this.lastClose) { + this.u = currentClose - this.lastClose; + this.d = 0; + } else { + this.u = 0; + this.d = this.lastClose - currentClose; + } + + this.avgU.update(this.u); + this.avgD.update(this.d); + + this.rs = this.avgU.result / this.avgD.result; + this.result = 100 - (100 / (1 + this.rs)); + + if (this.avgD.result === 0 && this.avgU.result !== 0) { + this.result = 100; + } else if (this.avgD.result === 0) { + this.result = 0; + } + + this.lastClose = currentClose; + this.age++; +} + +module.exports = Indicator; diff --git a/strategies/indicators/SMA.js b/strategies/indicators/SMA.js new file mode 100644 index 0000000..9eb1ef1 --- /dev/null +++ b/strategies/indicators/SMA.js @@ -0,0 +1,21 @@ +// required indicators +// Simple Moving Average - O(1) implementation + +var Indicator = function(windowLength) { + this.input = 'price'; + this.windowLength = windowLength; + this.prices = []; + this.result = 0; + this.age = 0; + this.sum = 0; +} + +Indicator.prototype.update = function(price) { + var tail = this.prices[this.age] || 0; // oldest price in window + this.prices[this.age] = price; + this.sum += price - tail; + this.result = this.sum / this.prices.length; + this.age = (this.age + 1) % this.windowLength +} + +module.exports = Indicator; diff --git a/strategies/indicators/SMMA.js b/strategies/indicators/SMMA.js new file mode 100644 index 0000000..f714c99 --- /dev/null +++ b/strategies/indicators/SMMA.js @@ -0,0 +1,28 @@ +// required indicators +var SMA = require('./SMA'); + +var Indicator = function (weight) { + this.input = 'price'; + this.sma = new SMA(weight); + this.weight = weight; + this.prices = []; + this.result = 0; + this.age = 0; +} + +Indicator.prototype.update = function (price) { + this.prices[this.age] = price; + + if(this.prices.length < this.weight) { + this.sma.update(price); + } else if(this.prices.length === this.weight) { + this.sma.update(price); + this.result = this.sma.result; + } else { + this.result = (this.result * (this.weight - 1) + price) / this.weight; + } + + this.age++; +} + +module.exports = Indicator; diff --git a/strategies/indicators/TSI.js b/strategies/indicators/TSI.js new file mode 100644 index 0000000..9cf8ee5 --- /dev/null +++ b/strategies/indicators/TSI.js @@ -0,0 +1,38 @@ +// required indicators +var EMA = require('./EMA.js'); + +var Indicator = function(settings) { + this.input = 'candle'; + this.lastClose = null; + this.tsi = 0; + this.inner = new EMA(settings.long); + this.outer = new EMA(settings.short); + this.absoluteInner = new EMA(settings.long); + this.absoluteOuter = new EMA(settings.short); +} + +Indicator.prototype.update = function(candle) { + var close = candle.close; + var prevClose = this.lastClose; + + if (prevClose === null) { + // Set initial price to prevent invalid change calculation + this.lastClose = close; + // Do not calculate TSI on first close + return; + } + + var momentum = close - prevClose; + + this.inner.update(momentum); + this.outer.update(this.inner.result); + + this.absoluteInner.update(Math.abs(momentum)); + this.absoluteOuter.update(this.absoluteInner.result); + + this.tsi = 100 * this.outer.result / this.absoluteOuter.result; + + this.lastClose = close; +} + +module.exports = Indicator; diff --git a/strategies/indicators/UO.js b/strategies/indicators/UO.js new file mode 100644 index 0000000..3183828 --- /dev/null +++ b/strategies/indicators/UO.js @@ -0,0 +1,45 @@ +// required indicators +var SMA = require('./SMA.js'); + +var Indicator = function(settings) { + this.input = 'candle'; + this.lastClose = 0; + this.uo = 0; + this.firstWeight = settings.first.weight; + this.secondWeight = settings.second.weight; + this.thirdWeight = settings.third.weight; + this.firstLow = new SMA(settings.first.period); + this.firstHigh = new SMA(settings.first.period); + this.secondLow = new SMA(settings.second.period); + this.secondHigh = new SMA(settings.second.period); + this.thirdLow = new SMA(settings.third.period); + this.thirdHigh = new SMA(settings.third.period); +} + +Indicator.prototype.update = function(candle) { + var close = candle.close; + var prevClose = this.lastClose; + var low = candle.low; + var high = candle.high; + + var bp = close - Math.min(low, prevClose); + var tr = Math.max(high, prevClose) - Math.min(low, prevClose); + + this.firstLow.update(tr); + this.secondLow.update(tr); + this.thirdLow.update(tr); + + this.firstHigh.update(bp); + this.secondHigh.update(bp); + this.thirdHigh.update(bp); + + var first = this.firstHigh.result / this.firstLow.result; + var second = this.secondHigh.result / this.secondLow.result; + var third = this.thirdHigh.result / this.thirdLow.result; + + this.uo = 100 * (this.firstWeight * first + this.secondWeight * second + this.thirdWeight * third) / (this.firstWeight + this.secondWeight + this.thirdWeight); + + this.lastClose = close; +} + +module.exports = Indicator; diff --git a/strategies/noop.js b/strategies/noop.js new file mode 100644 index 0000000..4e20b81 --- /dev/null +++ b/strategies/noop.js @@ -0,0 +1,13 @@ +// This method is a noop (it doesn't do anything) + +var _ = require('lodash'); + +// Let's create our own method +var method = {}; + +method.init = _.noop; +method.update = _.noop; +method.log = _.noop; +method.check = _.noop; + +module.exports = method; \ No newline at end of file diff --git a/strategies/talib-macd.js b/strategies/talib-macd.js new file mode 100644 index 0000000..64ed60e --- /dev/null +++ b/strategies/talib-macd.js @@ -0,0 +1,59 @@ +// If you want to use your own trading methods you can +// write them here. For more information on everything you +// can use please refer to this document: +// +// https://github.com/askmike/gekko/blob/stable/docs/trading_methods.md + +// Let's create our own method +var method = {}; + +// Prepare everything our method needs +method.init = function() { + this.name = 'talib-macd' + this.input = 'candle'; + // keep state about the current trend + // here, on every new candle we use this + // state object to check if we need to + // report it. + this.trend = 'none'; + + // how many candles do we need as a base + // before we can start giving advice? + this.requiredHistory = this.tradingAdvisor.historySize; + + var customMACDSettings = this.settings.parameters; + + // define the indicators we need + this.addTalibIndicator('mymacd', 'macd', customMACDSettings); +} + +// What happens on every new candle? +method.update = function(candle) { + // nothing! +} + + +method.log = function() { + // nothing! +} + +// Based on the newly calculated +// information, check if we should +// update or not. +method.check = function(candle) { + var price = candle.close; + var result = this.talibIndicators.mymacd.result; + var macddiff = result['outMACD'] - result['outMACDSignal']; + + if(this.settings.thresholds.down > macddiff && this.trend !== 'short') { + this.trend = 'short'; + this.advice('short'); + + } else if(this.settings.thresholds.up < macddiff && this.trend !== 'long'){ + this.trend = 'long'; + this.advice('long'); + + } +} + +module.exports = method; diff --git a/strategies/tulip-adx.js b/strategies/tulip-adx.js new file mode 100644 index 0000000..358af0b --- /dev/null +++ b/strategies/tulip-adx.js @@ -0,0 +1,30 @@ +var method = {}; +// Prepare everything our method needs +method.init = function() { + this.name = 'tulip-adx' + this.trend = 'none'; + this.requiredHistory = this.tradingAdvisor.historySize; + this.addTulipIndicator('myadx', 'adx', this.settings); +} +// What happens on every new candle? +method.update = function(candle) { + // nothing! +} +method.log = function() { + // nothing! +} +method.check = function(candle) { + var price = candle.close; + var adx = this.tulipIndicators.myadx.result.result; + // console.dir(adx) + + if(this.settings.thresholds.down > adx && this.trend !== 'short') { + this.trend = 'short'; + this.advice('short'); + } else if(this.settings.thresholds.up < adx && this.trend !== 'long'){ + this.trend = 'long'; + this.advice('long'); + } +} + +module.exports = method; diff --git a/strategies/tulip-macd.js b/strategies/tulip-macd.js new file mode 100644 index 0000000..7b2f43d --- /dev/null +++ b/strategies/tulip-macd.js @@ -0,0 +1,58 @@ +// If you want to use your own trading methods you can +// write them here. For more information on everything you +// can use please refer to this document: +// +// https://github.com/askmike/gekko/blob/stable/docs/trading_methods.md + +// Let's create our own method +var method = {}; + +// Prepare everything our method needs +method.init = function() { + this.name = 'tulip-macd' + // keep state about the current trend + // here, on every new candle we use this + // state object to check if we need to + // report it. + this.trend = 'none'; + + // how many candles do we need as a base + // before we can start giving advice? + this.requiredHistory = this.tradingAdvisor.historySize; + + var customMACDSettings = this.settings.parameters; + + // define the indicators we need + this.addTulipIndicator('mymacd', 'macd', customMACDSettings); +} + +// What happens on every new candle? +method.update = function(candle) { + // nothing! +} + + +method.log = function() { + // nothing! +} + +// Based on the newly calculated +// information, check if we should +// update or not. +method.check = function(candle) { + var price = candle.close; + var result = this.tulipIndicators.mymacd.result; + var macddiff = result['macd'] - result['macdSignal']; + + if(this.settings.thresholds.down > macddiff && this.trend !== 'short') { + this.trend = 'short'; + this.advice('short'); + + } else if(this.settings.thresholds.up < macddiff && this.trend !== 'long'){ + this.trend = 'long'; + this.advice('long'); + + } +} + +module.exports = method; diff --git a/strategies/tulip-multi-strat.js b/strategies/tulip-multi-strat.js new file mode 100644 index 0000000..753fdc8 --- /dev/null +++ b/strategies/tulip-multi-strat.js @@ -0,0 +1,57 @@ +var _ = require('lodash'); +var log = require('../core/log.js'); + +var method = {}; +method.init = function() { + // strat name + this.name = 'tulip-multi-strat'; + // trend information + this.trend = 'none' + // tulip indicators use this sometimes + this.requiredHistory = this.settings.historySize; + // define the indicators we need + this.addTulipIndicator('myadx', 'adx', this.settings); + this.addTulipIndicator('mymacd', 'macd', this.settings); +} + +// what happens on every new candle? +method.update = function(candle) { + // tulip results + this.adx = this.tulipIndicators.myadx.result.result; + this.macd = this.tulipIndicators.mymacd.result.macdHistogram; +} +// for debugging purposes log the last +// calculated parameters. +method.log = function() { + log.debug( +`--------------------- +Tulip ADX: ${this.adx} +Tulip MACD: ${this.macd} +`); +} + +method.check = function() { + // just add a long and short to each array when new indicators are used + const all_long = [ + this.adx > this.settings.up && this.trend!=='long', + this.settings.macd_up < this.macd && this.trend!=='long', + ].reduce((total, long)=>long && total, true) + const all_short = [ + this.adx < this.settings.down && this.trend!=='short', + this.settings.macd_down > this.macd && this.trend!=='short', + ].reduce((total, long)=>long && total, true) + + // combining all indicators with AND + if(all_long){ + log.debug(`tulip-multi-strat In low`); + this.advice('long'); + }else if(all_short){ + log.debug(`tulip-multi-strat In high`); + this.advice('short'); + }else{ + log.debug(`tulip-multi-strat In no trend`); + this.advice(); + } +} + +module.exports = method; diff --git a/strategies/varPPO.js b/strategies/varPPO.js new file mode 100644 index 0000000..3cd2536 --- /dev/null +++ b/strategies/varPPO.js @@ -0,0 +1,136 @@ +// helpers +var _ = require('lodash'); +var log = require('../core/log'); + +// configuration +var config = require('../core/util').getConfig(); +var settings = config.varPPO; +var momentum = settings.momentum; +var momentumName = momentum.toLowerCase(); +var momentumSettings = config[momentum]; + +// let's create our own method +var method = {}; + +// prepare everything our method needs +method.init = function() { + this.trend = { + direction: 'none', + duration: 0, + persisted: false, + adviced: false + }; + + this.requiredHistory = this.tradingAdvisor.historySize; + + // define the indicators we need + this.addIndicator('ppo', 'PPO', config.PPO); + this.addIndicator(momentumName, momentum, momentumSettings); +} + +// what happens on every new candle? +method.update = function(candle) { + // nothing! +} + +// for debugging purposes log the last +// calculated parameters. +method.log = function(candle) { + var digits = 8; + var ppo = this.indicators.ppo.result; + var result = ppo.ppo; + var signal = ppo.PPOsignal; + var hist = ppo.PPOhist; + var momentumResult = this.indicators[momentumName][momentumName]; + + log.debug('\t', 'PPO:', result.toFixed(digits)); + log.debug('\t', 'PPOsignal:', signal.toFixed(digits)); + log.debug('\t', 'PPOhist:', hist.toFixed(digits)); + log.debug('\t', momentum + ':', momentumResult.toFixed(digits)); + log.debug('\t', 'price:', candle.close.toFixed(digits)); +} + +method.check = function() { + var ppo = this.indicators.ppo.result; + var hist = ppo.PPOhist; + + var value = this.indicators[momentumName][momentumName]; + + var thresholds = { + low: momentumSettings.thresholds.low + hist * settings.thresholds.weightLow, + high: momentumSettings.thresholds.high + hist * settings.thresholds.weightHigh + }; + + if(value < thresholds.low) { + + // new trend detected + if(this.trend.direction !== 'up') + this.trend = { + duration: 0, + persisted: false, + direction: 'up', + adviced: false + }; + + this.trend.duration++; + + log.debug('In uptrend since', this.trend.duration, 'candle(s)'); + + if(this.trend.duration >= settings.thresholds.persistence) + this.trend.persisted = true; + + if(this.trend.persisted && !this.trend.adviced) { + this.trend.adviced = true; + this.advice('long'); + } else + this.advice(); + + } else if(value > thresholds.high) { + + // new trend detected + if(this.trend.direction !== 'down') + this.trend = { + duration: 0, + persisted: false, + direction: 'down', + adviced: false + }; + + this.trend.duration++; + + log.debug('In downtrend since', this.trend.duration, 'candle(s)'); + + if(this.trend.duration >= settings.thresholds.persistence) + this.trend.persisted = true; + + if(this.trend.persisted && !this.trend.adviced) { + this.trend.adviced = true; + this.advice('short'); + } else + this.advice(); + + + } else { + + log.debug('In no trend'); + + // we're not in an up nor in a downtrend + // but for now we ignore sideways trends + // + // read more @link: + // + // https://github.com/askmike/gekko/issues/171 + + // this.trend = { + // direction: 'none', + // duration: 0, + // persisted: false, + // adviced: false + // }; + + this.advice(); + } + +} + +module.exports = method; diff --git a/subscriptions.js b/subscriptions.js new file mode 100644 index 0000000..6c5fc58 --- /dev/null +++ b/subscriptions.js @@ -0,0 +1,114 @@ +// +// Subscriptions glue plugins to events +// flowing through the Gekko. +// + +var subscriptions = [ + { + emitter: 'market', + event: 'candle', + handler: 'processCandle' + }, + { + emitter: 'market', + event: 'marketUpdate', + handler: 'processMarketUpdate' + }, + { + emitter: 'market', + event: 'marketStart', + handler: 'processMarketStart' + }, + { + emitter: 'tradingAdvisor', + event: 'stratWarmupCompleted', + handler: 'processStratWarmupCompleted' + }, + { + emitter: 'tradingAdvisor', + event: 'advice', + handler: 'processAdvice' + }, + { + emitter: 'tradingAdvisor', + event: 'stratCandle', + handler: 'processStratCandle' + }, + { + emitter: 'tradingAdvisor', + event: 'stratUpdate', + handler: 'processStratUpdate' + }, + { + emitter: 'tradingAdvisor', + event: 'stratNotification', + handler: 'processStratNotification' + }, + { + emitter: ['trader', 'paperTrader'], + event: 'tradeInitiated', + handler: 'processTradeInitiated' + }, + { + emitter: ['trader', 'paperTrader'], + event: 'tradeAborted', + handler: 'processTradeAborted' + }, + { + emitter: ['trader', 'paperTrader'], + event: 'tradeCompleted', + handler: 'processTradeCompleted' + }, + { + emitter: 'trader', + event: 'tradeCancelled', + handler: 'processTradeCancelled' + }, + { + emitter: 'trader', + event: 'tradeErrored', + handler: 'processTradeErrored' + }, + { + emitter: ['trader', 'paperTrader'], + event: 'portfolioChange', + handler: 'processPortfolioChange' + }, + { + emitter: ['trader', 'paperTrader'], + event: 'triggerCreated', + handler: 'processTriggerCreated' + }, + { + emitter: ['trader', 'paperTrader'], + event: 'triggerAborted', + handler: 'processTriggerAborted' + }, + { + emitter: ['trader', 'paperTrader'], + event: 'triggerFired', + handler: 'processTriggerFired' + }, + { + emitter: ['trader', 'paperTrader'], + event: 'portfolioValueChange', + handler: 'processPortfolioValueChange' + }, + { + emitter: 'performanceAnalyzer', + event: 'performanceReport', + handler: 'processPerformanceReport' + }, + { + emitter: 'performanceAnalyzer', + event: 'roundtripUpdate', + handler: 'processRoundtripUpdate' + }, + { + emitter: 'performanceAnalyzer', + event: 'roundtrip', + handler: 'processRoundtrip' + }, +]; + +module.exports = subscriptions; \ No newline at end of file diff --git a/test/_prepare.js b/test/_prepare.js new file mode 100644 index 0000000..17eb18f --- /dev/null +++ b/test/_prepare.js @@ -0,0 +1,5 @@ +// overwrite config with test-config + +var utils = require(__dirname + '/../core/util'); +var testConfig = require(__dirname + '/test-config.json'); +utils.setConfig(testConfig); \ No newline at end of file diff --git a/test/candleBatcher.js b/test/candleBatcher.js new file mode 100644 index 0000000..06013ca --- /dev/null +++ b/test/candleBatcher.js @@ -0,0 +1,104 @@ +var chai = require('chai'); +var expect = chai.expect; +var should = chai.should; +var sinon = require('sinon'); + +var _ = require('lodash'); +var moment = require('moment'); + +var utils = require(__dirname + '/../core/util'); + +var dirs = utils.dirs(); +var CandleBatcher = require(dirs.core + 'candleBatcher'); + +var candles = [ + {id: 1, "start":moment("2015-02-14T23:57:00.000Z"),"open":257.19,"high":257.19,"low":257.18,"close":257.18,"vwp":257.18559990418294,"volume":0.97206065,"trades":2}, + {id: 2, "start":moment("2015-02-14T23:58:00.000Z"),"open":257.02,"high":257.02,"low":256.98,"close":256.98,"vwp":257.0175849772836,"volume":4.1407478,"trades":2}, + {id: 3, "start":moment("2015-02-14T23:59:00.000Z"),"open":256.85,"high":256.99,"low":256.85,"close":256.99,"vwp":256.9376998467,"volume":6,"trades":6}, + {id: 4, "start":moment("2015-02-15T00:00:00.000Z"),"open":256.81,"high":256.82,"low":256.81,"close":256.82,"vwp":256.815,"volume":4,"trades":2}, + {id: 5, "start":moment("2015-02-15T00:01:00.000Z"),"open":256.81,"high":257.02,"low":256.81,"close":257.01,"vwp":256.94666666666666,"volume":6,"trades":3}, + {id: 6, "start":moment("2015-02-15T00:02:00.000Z"),"open":257.03,"high":257.03,"low":256.33,"close":256.33,"vwp":256.74257263558013,"volume":6.7551178,"trades":6}, + {id: 7, "start":moment("2015-02-15T00:03:00.000Z"),"open":257.02,"high":257.47,"low":257.02,"close":257.47,"vwp":257.26466004728906,"volume":3.7384995300000003,"trades":3}, + {id: 8, "start":moment("2015-02-15T00:04:00.000Z"),"open":257.47,"high":257.48,"low":257.37,"close":257.38,"vwp":257.4277429116875,"volume":8,"trades":6}, + {id: 9, "start":moment("2015-02-15T00:05:00.000Z"),"open":257.38,"high":257.45,"low":257.38,"close":257.45,"vwp":257.3975644932184,"volume":7.97062564,"trades":4}, + {id: 10, "start":moment("2015-02-15T00:06:00.000Z"),"open":257.46,"high":257.48,"low":257.46,"close":257.48,"vwp":257.47333333333336,"volume":7.5,"trades":4} +]; + +describe('core/candleBatcher', function() { + var cb; + + it('should throw when not passed a number', function() { + expect(function() { + new CandleBatcher(); + }).to.throw('candleSize is not a number'); + }); + + it('should instantiate', function() { + cb = new CandleBatcher(2); + }); + + it('should throw when fed a candle', function() { + var candle = _.first(candles); + expect( + cb.write.bind(cb, candle) + ).to.throw('candles is not an array'); + }); + + it('should not emit an event when fed not enough candles', function() { + var candle = _.first(candles); + + var spy = sinon.spy(); + cb.on('candle', spy); + cb.write( [candle] ); + expect(spy.called).to.be.false; + }); + + it('should not emit an event when not flushed', function() { + cb = new CandleBatcher(2); + + var spy = sinon.spy(); + cb.on('candle', spy); + cb.write( candles ); + expect(spy.called).to.be.false; + }); + + it('should emit 5 events when fed 10 candles', function() { + cb = new CandleBatcher(2); + + var spy = sinon.spy(); + cb.on('candle', spy); + cb.write( candles ); + cb.flush(); + expect(spy.callCount).to.equal(5); + }); + + it('should correctly add two candles together', function() { + cb = new CandleBatcher(2); + var _candles = _.first(candles, 2); + var first = _.first(_candles); + var second = _.last(_candles); + + var result = { + start: first.start, + open: first.open, + high: _.max([first.high, second.high]), + low: _.min([first.low, second.low]), + close: second.close, + volume: first.volume + second.volume, + vwp: (first.vwp * first.volume) + (second.vwp * second.volume), + trades: first.trades + second.trades + }; + + result.vwp /= result.volume; + + var spy = sinon.spy(); + cb.on('candle', spy); + cb.write( _candles ); + cb.flush(); + + var cbResult = _.first(_.first(spy.args)); + expect(cbResult).to.deep.equal(result); + expect(cbResult.id).to.equal(undefined); + }); + +}); \ No newline at end of file diff --git a/test/exchanges/bitstamp.js b/test/exchanges/bitstamp.js new file mode 100644 index 0000000..147ba18 --- /dev/null +++ b/test/exchanges/bitstamp.js @@ -0,0 +1,114 @@ + +// if you need to test Gekko against real mocked data +// uncomment the following: + +// var fs = require('fs'); +// var bitstamp = require('bitstamp'); +// var bs = new bitstamp; +// bs.transactions('btcusd', (err, data) => { +// if(err) +// throw err; + +// var json = JSON.stringify(data, null, 4); +// fs.writeFileSync('./data/bitstamp_trades.json', json); +// }); +// return; + +var chai = require('chai'); +var expect = chai.expect; +var should = chai.should; +var sinon = require('sinon'); +var proxyquire = require('proxyquire'); + +var _ = require('lodash'); +var moment = require('moment'); + +var util = require(__dirname + '/../../core/util'); +var config = util.getConfig(); +var dirs = util.dirs(); + +var TRADES = require('./data/bitstamp_trades.json'); + +return; // TEMP + +var FakeExchange = function() {}; +FakeExchange.prototype = { + transactions: function(since, handler, descending) { + handler( + null, + TRADES + ); + } +} +var transactionsSpy = sinon.spy(FakeExchange.prototype, 'transactions'); +spoofer = { + bitstamp: FakeExchange +} + +describe('exchanges/bitstamp', function() { + var Bitstamp = proxyquire(dirs.gekko + 'exchange/wrappers/bitstamp', spoofer); + var bs; + + it('should instantiate', function() { + bs = new Bitstamp(config.watch); + }); + + it('should correctly fetch historical trades', function() { + bs.getTrades(null, _.noop, false); + + expect(transactionsSpy.callCount).to.equal(1); + + var args = transactionsSpy.lastCall.args; + expect(args.length).to.equal(2); + + expect(args[0]).to.equal('btcusd'); + }); + + it('should retry on exchange error', function() { + var ErrorFakeExchange = function() {}; + ErrorFakeExchange.prototype = { + transactions: function(since, handler, descending) { + handler('Auth error'); + } + } + spoofer = { + bitstamp: ErrorFakeExchange + } + + var ErroringBitstamp = proxyquire(dirs.exchanges + 'bitstamp', spoofer); + var ebs = new ErroringBitstamp(config.watch); + + ebs.retry = _.noop; + var retrySpy = sinon.spy(ebs, 'retry'); + + ebs.getTrades(null, _.noop) + + expect(retrySpy.callCount).to.equal(1); + + var args = retrySpy.lastCall.args; + expect(args[1].length).to.equal(2); + expect(args[1][0]).to.equal(null); + }) + + it('should correctly parse historical trades', function(done) { + var check = function(err, trades) { + + expect(err).to.equal(null); + + expect(trades.length).to.equal(TRADES.length); + + var oldest = _.first(trades); + var OLDEST = _.last(TRADES); + + expect(oldest.tid).to.equal(+OLDEST.tid); + expect(oldest.price).to.equal(+OLDEST.price); + expect(oldest.amount).to.equal(+OLDEST.amount); + expect(oldest.date).to.equal(OLDEST.date); + + done(); + } + + bs.getTrades(null, check, false); + + }); +}); \ No newline at end of file diff --git a/test/exchanges/data/bitstamp_trades.json b/test/exchanges/data/bitstamp_trades.json new file mode 100644 index 0000000..7dc4a61 --- /dev/null +++ b/test/exchanges/data/bitstamp_trades.json @@ -0,0 +1,947 @@ +[ + { + "date": "1476099635", + "tid": "12166560", + "price": "612.91000", + "type": "1", + "amount": "0.05563331" + }, + { + "date": "1476099613", + "tid": "12166552", + "price": "614.20000", + "type": "0", + "amount": "0.65581425" + }, + { + "date": "1476099574", + "tid": "12166551", + "price": "612.91000", + "type": "1", + "amount": "0.08348263" + }, + { + "date": "1476099544", + "tid": "12166550", + "price": "614.20000", + "type": "0", + "amount": "0.05827100" + }, + { + "date": "1476099523", + "tid": "12166548", + "price": "612.91000", + "type": "1", + "amount": "0.05563382" + }, + { + "date": "1476099355", + "tid": "12166542", + "price": "614.20000", + "type": "0", + "amount": "0.81406708" + }, + { + "date": "1476099246", + "tid": "12166541", + "price": "614.20000", + "type": "0", + "amount": "0.02000000" + }, + { + "date": "1476099156", + "tid": "12166539", + "price": "614.20000", + "type": "0", + "amount": "0.39384817" + }, + { + "date": "1476099154", + "tid": "12166538", + "price": "614.15000", + "type": "0", + "amount": "0.48832321" + }, + { + "date": "1476099137", + "tid": "12166537", + "price": "614.15000", + "type": "0", + "amount": "0.02000000" + }, + { + "date": "1476099099", + "tid": "12166534", + "price": "614.15000", + "type": "0", + "amount": "0.02300000" + }, + { + "date": "1476099067", + "tid": "12166525", + "price": "614.15000", + "type": "0", + "amount": "0.07519000" + }, + { + "date": "1476099058", + "tid": "12166524", + "price": "612.76000", + "type": "1", + "amount": "0.03708453" + }, + { + "date": "1476099045", + "tid": "12166523", + "price": "614.15000", + "type": "0", + "amount": "0.06303310" + }, + { + "date": "1476098972", + "tid": "12166519", + "price": "614.15000", + "type": "1", + "amount": "0.11617019" + }, + { + "date": "1476098971", + "tid": "12166518", + "price": "614.20000", + "type": "0", + "amount": "0.02300000" + }, + { + "date": "1476098943", + "tid": "12166517", + "price": "614.20000", + "type": "0", + "amount": "0.28252528" + }, + { + "date": "1476098748", + "tid": "12166497", + "price": "614.20000", + "type": "0", + "amount": "0.07519000" + }, + { + "date": "1476098707", + "tid": "12166490", + "price": "612.99000", + "type": "0", + "amount": "0.12971692" + }, + { + "date": "1476098707", + "tid": "12166489", + "price": "612.99000", + "type": "0", + "amount": "0.06289098" + }, + { + "date": "1476098616", + "tid": "12166487", + "price": "612.73000", + "type": "1", + "amount": "0.47650000" + }, + { + "date": "1476098550", + "tid": "12166482", + "price": "612.99000", + "type": "0", + "amount": "0.04296970" + }, + { + "date": "1476098544", + "tid": "12166480", + "price": "612.99000", + "type": "0", + "amount": "0.05113932" + }, + { + "date": "1476098544", + "tid": "12166477", + "price": "612.99000", + "type": "0", + "amount": "0.09140786" + }, + { + "date": "1476098538", + "tid": "12166474", + "price": "612.99000", + "type": "0", + "amount": "0.06517154" + }, + { + "date": "1476098538", + "tid": "12166473", + "price": "612.99000", + "type": "0", + "amount": "0.07737564" + }, + { + "date": "1476098532", + "tid": "12166470", + "price": "612.99000", + "type": "0", + "amount": "0.14254718" + }, + { + "date": "1476098526", + "tid": "12166466", + "price": "612.99000", + "type": "0", + "amount": "0.14254718" + }, + { + "date": "1476098488", + "tid": "12166463", + "price": "612.97000", + "type": "1", + "amount": "0.12971692" + }, + { + "date": "1476098488", + "tid": "12166462", + "price": "612.97000", + "type": "1", + "amount": "0.15700000" + }, + { + "date": "1476098488", + "tid": "12166461", + "price": "612.97000", + "type": "1", + "amount": "0.13047962" + }, + { + "date": "1476098477", + "tid": "12166460", + "price": "614.20000", + "type": "0", + "amount": "0.02701517" + }, + { + "date": "1476098476", + "tid": "12166458", + "price": "614.18000", + "type": "0", + "amount": "0.09610000" + }, + { + "date": "1476098476", + "tid": "12166457", + "price": "614.18000", + "type": "0", + "amount": "0.15600000" + }, + { + "date": "1476098476", + "tid": "12166456", + "price": "614.18000", + "type": "0", + "amount": "0.07639355" + }, + { + "date": "1476098403", + "tid": "12166448", + "price": "614.18000", + "type": "0", + "amount": "0.04128385" + }, + { + "date": "1476098403", + "tid": "12166447", + "price": "614.18000", + "type": "0", + "amount": "0.15800000" + }, + { + "date": "1476098403", + "tid": "12166446", + "price": "614.18000", + "type": "0", + "amount": "0.15800000" + }, + { + "date": "1476098403", + "tid": "12166445", + "price": "614.18000", + "type": "0", + "amount": "0.17900000" + }, + { + "date": "1476098403", + "tid": "12166444", + "price": "614.18000", + "type": "0", + "amount": "0.41000000" + }, + { + "date": "1476098403", + "tid": "12166443", + "price": "614.18000", + "type": "0", + "amount": "0.05371615" + }, + { + "date": "1476098318", + "tid": "12166437", + "price": "612.97000", + "type": "1", + "amount": "0.01652038" + }, + { + "date": "1476098176", + "tid": "12166429", + "price": "614.18000", + "type": "0", + "amount": "0.15665000" + }, + { + "date": "1476098173", + "tid": "12166428", + "price": "612.78000", + "type": "1", + "amount": "0.11767740" + }, + { + "date": "1476098173", + "tid": "12166427", + "price": "612.78000", + "type": "1", + "amount": "0.15600000" + }, + { + "date": "1476098172", + "tid": "12166426", + "price": "612.79000", + "type": "1", + "amount": "0.09610000" + }, + { + "date": "1476098104", + "tid": "12166421", + "price": "614.18000", + "type": "0", + "amount": "0.03001800" + }, + { + "date": "1476098042", + "tid": "12166412", + "price": "614.18000", + "type": "0", + "amount": "0.01878000" + }, + { + "date": "1476097969", + "tid": "12166408", + "price": "614.18000", + "type": "0", + "amount": "0.02000000" + }, + { + "date": "1476097943", + "tid": "12166407", + "price": "614.18000", + "type": "0", + "amount": "0.12783585" + }, + { + "date": "1476097943", + "tid": "12166406", + "price": "614.18000", + "type": "0", + "amount": "0.15900000" + }, + { + "date": "1476097943", + "tid": "12166405", + "price": "614.18000", + "type": "0", + "amount": "0.15800000" + }, + { + "date": "1476097942", + "tid": "12166404", + "price": "614.18000", + "type": "0", + "amount": "0.49381415" + }, + { + "date": "1476097814", + "tid": "12166401", + "price": "614.18000", + "type": "0", + "amount": "0.15080751" + }, + { + "date": "1476097813", + "tid": "12166400", + "price": "614.18000", + "type": "0", + "amount": "0.23230000" + }, + { + "date": "1476097811", + "tid": "12166399", + "price": "614.18000", + "type": "0", + "amount": "0.18140000" + }, + { + "date": "1476097803", + "tid": "12166398", + "price": "612.95000", + "type": "1", + "amount": "0.16268000" + }, + { + "date": "1476097643", + "tid": "12166387", + "price": "612.66000", + "type": "1", + "amount": "2.14279194" + }, + { + "date": "1476097643", + "tid": "12166386", + "price": "612.99000", + "type": "1", + "amount": "2.30935500" + }, + { + "date": "1476097643", + "tid": "12166385", + "price": "613.24000", + "type": "1", + "amount": "3.02659541" + }, + { + "date": "1476097643", + "tid": "12166384", + "price": "613.26000", + "type": "1", + "amount": "1.05795023" + }, + { + "date": "1476097642", + "tid": "12166383", + "price": "614.09000", + "type": "1", + "amount": "1.28080550" + }, + { + "date": "1476097618", + "tid": "12166382", + "price": "614.20000", + "type": "0", + "amount": "0.91402550" + }, + { + "date": "1476097617", + "tid": "12166381", + "price": "614.18000", + "type": "0", + "amount": "0.28027603" + }, + { + "date": "1476097581", + "tid": "12166379", + "price": "614.18000", + "type": "0", + "amount": "0.27632529" + }, + { + "date": "1476097567", + "tid": "12166378", + "price": "614.09000", + "type": "1", + "amount": "0.02178692" + }, + { + "date": "1476097540", + "tid": "12166375", + "price": "614.09000", + "type": "0", + "amount": "0.15700000" + }, + { + "date": "1476097540", + "tid": "12166373", + "price": "614.09000", + "type": "0", + "amount": "0.15800000" + }, + { + "date": "1476097540", + "tid": "12166372", + "price": "614.09000", + "type": "0", + "amount": "0.15800000" + }, + { + "date": "1476097540", + "tid": "12166370", + "price": "614.09000", + "type": "0", + "amount": "0.15800000" + }, + { + "date": "1476097540", + "tid": "12166369", + "price": "614.09000", + "type": "0", + "amount": "0.15800000" + }, + { + "date": "1476097539", + "tid": "12166368", + "price": "614.09000", + "type": "0", + "amount": "0.09484126" + }, + { + "date": "1476097536", + "tid": "12166366", + "price": "614.09000", + "type": "0", + "amount": "0.06315874" + }, + { + "date": "1476097536", + "tid": "12166365", + "price": "614.09000", + "type": "0", + "amount": "0.14600000" + }, + { + "date": "1476097536", + "tid": "12166364", + "price": "614.09000", + "type": "0", + "amount": "0.41000000" + }, + { + "date": "1476097536", + "tid": "12166363", + "price": "614.09000", + "type": "0", + "amount": "0.40600000" + }, + { + "date": "1476097535", + "tid": "12166362", + "price": "614.09000", + "type": "0", + "amount": "0.40600000" + }, + { + "date": "1476097535", + "tid": "12166361", + "price": "614.09000", + "type": "0", + "amount": "0.40600000" + }, + { + "date": "1476097535", + "tid": "12166360", + "price": "614.09000", + "type": "0", + "amount": "0.40900000" + }, + { + "date": "1476097535", + "tid": "12166359", + "price": "614.09000", + "type": "0", + "amount": "0.40600000" + }, + { + "date": "1476097535", + "tid": "12166358", + "price": "614.09000", + "type": "0", + "amount": "0.15800000" + }, + { + "date": "1476097535", + "tid": "12166357", + "price": "614.09000", + "type": "0", + "amount": "0.15800000" + }, + { + "date": "1476097535", + "tid": "12166356", + "price": "614.09000", + "type": "0", + "amount": "0.15900000" + }, + { + "date": "1476097534", + "tid": "12166355", + "price": "614.09000", + "type": "0", + "amount": "0.15800000" + }, + { + "date": "1476097534", + "tid": "12166354", + "price": "614.09000", + "type": "0", + "amount": "0.40600000" + }, + { + "date": "1476097529", + "tid": "12166353", + "price": "613.32000", + "type": "1", + "amount": "2.90000000" + }, + { + "date": "1476097503", + "tid": "12166350", + "price": "612.99000", + "type": "1", + "amount": "1.59260664" + }, + { + "date": "1476097503", + "tid": "12166349", + "price": "613.06000", + "type": "1", + "amount": "0.14700000" + }, + { + "date": "1476097502", + "tid": "12166348", + "price": "613.23000", + "type": "1", + "amount": "4.90000000" + }, + { + "date": "1476097502", + "tid": "12166347", + "price": "613.43000", + "type": "1", + "amount": "0.15600000" + }, + { + "date": "1476097502", + "tid": "12166346", + "price": "613.43000", + "type": "1", + "amount": "0.14700000" + }, + { + "date": "1476097501", + "tid": "12166345", + "price": "613.55000", + "type": "1", + "amount": "0.15700000" + }, + { + "date": "1476097501", + "tid": "12166344", + "price": "613.58000", + "type": "1", + "amount": "4.38220000" + }, + { + "date": "1476097500", + "tid": "12166343", + "price": "614.11000", + "type": "1", + "amount": "0.48351110" + }, + { + "date": "1476097492", + "tid": "12166342", + "price": "614.19000", + "type": "1", + "amount": "0.50000000" + }, + { + "date": "1476097484", + "tid": "12166341", + "price": "614.20000", + "type": "1", + "amount": "31.92160000" + }, + { + "date": "1476097484", + "tid": "12166340", + "price": "614.22000", + "type": "1", + "amount": "0.50000000" + }, + { + "date": "1476097364", + "tid": "12166331", + "price": "614.20000", + "type": "1", + "amount": "1.95240650" + }, + { + "date": "1476097364", + "tid": "12166330", + "price": "614.52000", + "type": "1", + "amount": "1.05521350" + }, + { + "date": "1476097200", + "tid": "12166323", + "price": "614.76000", + "type": "0", + "amount": "0.08845994" + }, + { + "date": "1476097171", + "tid": "12166321", + "price": "614.76000", + "type": "0", + "amount": "0.09385000" + }, + { + "date": "1476097127", + "tid": "12166320", + "price": "614.38000", + "type": "1", + "amount": "0.02178327" + }, + { + "date": "1476097028", + "tid": "12166312", + "price": "615.18000", + "type": "0", + "amount": "0.49433382" + }, + { + "date": "1476097028", + "tid": "12166311", + "price": "615.18000", + "type": "0", + "amount": "0.28054507" + }, + { + "date": "1476097027", + "tid": "12166310", + "price": "615.17000", + "type": "1", + "amount": "0.04000000" + }, + { + "date": "1476096998", + "tid": "12166308", + "price": "614.11000", + "type": "1", + "amount": "0.01648890" + }, + { + "date": "1476096994", + "tid": "12166307", + "price": "615.17000", + "type": "0", + "amount": "0.01732453" + }, + { + "date": "1476096785", + "tid": "12166297", + "price": "614.15000", + "type": "0", + "amount": "4.36590263" + }, + { + "date": "1476096782", + "tid": "12166295", + "price": "614.17000", + "type": "1", + "amount": "1.62632737" + }, + { + "date": "1476096671", + "tid": "12166286", + "price": "615.18000", + "type": "0", + "amount": "0.77605625" + }, + { + "date": "1476096671", + "tid": "12166285", + "price": "615.18000", + "type": "0", + "amount": "1.17641295" + }, + { + "date": "1476096532", + "tid": "12166284", + "price": "615.18000", + "type": "0", + "amount": "0.45000000" + }, + { + "date": "1476096518", + "tid": "12166283", + "price": "614.12000", + "type": "1", + "amount": "0.82273414" + }, + { + "date": "1476096518", + "tid": "12166282", + "price": "614.60000", + "type": "1", + "amount": "0.39143007" + }, + { + "date": "1476096475", + "tid": "12166281", + "price": "615.02000", + "type": "1", + "amount": "9.64860000" + }, + { + "date": "1476096453", + "tid": "12166279", + "price": "614.99000", + "type": "1", + "amount": "4.12628026" + }, + { + "date": "1476096450", + "tid": "12166278", + "price": "614.99000", + "type": "0", + "amount": "0.15800000" + }, + { + "date": "1476096450", + "tid": "12166277", + "price": "614.99000", + "type": "0", + "amount": "0.15800000" + }, + { + "date": "1476096450", + "tid": "12166276", + "price": "614.99000", + "type": "0", + "amount": "0.15800000" + }, + { + "date": "1476096450", + "tid": "12166275", + "price": "614.99000", + "type": "0", + "amount": "0.40900000" + }, + { + "date": "1476096450", + "tid": "12166274", + "price": "614.99000", + "type": "0", + "amount": "0.30145000" + }, + { + "date": "1476096370", + "tid": "12166265", + "price": "614.99000", + "type": "0", + "amount": "0.10655000" + }, + { + "date": "1476096370", + "tid": "12166264", + "price": "614.99000", + "type": "0", + "amount": "0.05000000" + }, + { + "date": "1476096292", + "tid": "12166258", + "price": "615.17000", + "type": "0", + "amount": "2.13738013" + }, + { + "date": "1476096292", + "tid": "12166257", + "price": "614.97000", + "type": "0", + "amount": "0.86261987" + }, + { + "date": "1476096172", + "tid": "12166241", + "price": "614.97000", + "type": "0", + "amount": "3.57370278" + }, + { + "date": "1476096171", + "tid": "12166240", + "price": "614.91000", + "type": "0", + "amount": "2.98402000" + }, + { + "date": "1476096171", + "tid": "12166239", + "price": "614.90000", + "type": "0", + "amount": "0.24000000" + }, + { + "date": "1476096171", + "tid": "12166238", + "price": "614.84000", + "type": "0", + "amount": "0.34034948" + }, + { + "date": "1476096170", + "tid": "12166237", + "price": "614.84000", + "type": "0", + "amount": "0.11200000" + }, + { + "date": "1476096170", + "tid": "12166236", + "price": "614.84000", + "type": "0", + "amount": "0.15800000" + }, + { + "date": "1476096170", + "tid": "12166235", + "price": "614.84000", + "type": "0", + "amount": "0.15800000" + }, + { + "date": "1476096170", + "tid": "12166234", + "price": "614.84000", + "type": "0", + "amount": "0.15700000" + }, + { + "date": "1476096169", + "tid": "12166233", + "price": "614.84000", + "type": "0", + "amount": "0.40800000" + }, + { + "date": "1476096154", + "tid": "12166232", + "price": "614.70000", + "type": "1", + "amount": "0.46887000" + } +] \ No newline at end of file diff --git a/test/indicators/cci.js b/test/indicators/cci.js new file mode 100644 index 0000000..6cab4b6 --- /dev/null +++ b/test/indicators/cci.js @@ -0,0 +1,59 @@ +var chai = require('chai'); +var expect = chai.expect; +var should = chai.should; +var sinon = require('sinon'); + +var _ = require('lodash'); + +var util = require('../../core/util'); +var dirs = util.dirs(); +var INDICATOR_PATH = dirs.indicators; + +// Fake input prices to verify all indicators +// are working correctly by comparing fresh +// calculated results to pre calculated results. + +// Since there are is no CCI Indicator @ https://github.com/wuhkuh/talib, +// the precalculated results have been calculated with Excel :D + +var high = [86, 85, 90, 95, 58, 71, 84, 98, 99, 100, 91, 95, 99, 96, 96, 90, 79, 72, 91, 81, 94, 87, 60, 93, 75, 94, 75, 93, 92, 92, 99, 99, 84, 91, 27, 67, 97, 92, 89]; +var low = [1, 5, 14, 10, 20, 10, 1, 48, 64, 0, 0, 50, 15, 0, 4, 57, 1, 11, 9, 1, 1, 10, 25, 11, 16, 50, 7, 13, 74, 71, 59, 3, 1, 1, 0, 5, 1, 60, 6]; +var close = [81, 24, 75, 21, 34, 25, 72, 92, 99, 2, 86, 80, 76, 8, 87, 75, 32, 65, 41, 9, 13, 26, 56, 28, 65, 58, 17, 90, 87, 86, 99, 3, 70, 1, 27, 9, 92, 68, 9]; + +describe('indicators/CCI', function() { + + var CCI = require(INDICATOR_PATH + 'CCI'); + + //Had to limit the decimals because of rounding mistakes + var verified_cci8results = [0, 0, 0, 0, 0, 0, 0, 165.258216, 137.978495, -78.352249, 23.011844, 66.039216, 11.260054, -110.755556, 2.108795, 60.958054, -79.830149, -40.800225, -42.00627, -103.175918, -58.657244, -37.232704, 12.844037, 31.25, 101.960784, 187.096774, -82.539683, 114.047867, 147.875064, 101.075269, 88.888889, -108.039098, -43.914081, -87.872763, -113.138686, -68.525669, 43.243243, 82.481254, -29.942756]; + var verified_cci10results = [0, 0, 0, 0, 0, 0, 0, 0, 0, -81.681682, 28.953557, 78.940028, 27.948194, -82.58317, 17.665798, 57.71725, -100.171969, -36.79078, -33.502538, -113.08642, -70.175439, -36.995092, 7.30897, -20.757021, 51.239669, 200, -99.574468, 134.51119, 166.021297, 117.174281, 100.395257, -94.147583, -33.202614, -101.396478, -115.00256, -71.976401, 27.092846, 53.157122, -49.211356]; + var verified_cci12results = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 91.97995, 35.138387, -86.288416, 30.06993, 67.713787, -85.863874, -44.274809, -51.105651, -102.536873, -68.659725, -49.02507, -14.705882, -21.217712, 42.461538, 122.124711, -94.10628, 146.91745, 197.464342, 135.599506, 111.925175, -80.55041, -23.476298, -99.837574, -126.229111, -84.671533, 30.350877, 58.954584, -61.147046]; + + it('should correctly calculate CCI with period 8', function() { + var cci = new CCI({ history: 8, constant: 0.015 }); + _.each(close,function(p,i) { + cci.update({high: high[i], low: low[i] , close: close[i]}); + //when i { + let cb; + + it('should instantiate a trailing stop loss trigger', () => { + expect(() => { + const ts = new TrailingStop({ + trail: 10, + initialPrice: 100, + onTrigger: () => {} + }) + }).to.not.throw() + }) + + it('should call onTrigger when the stop hits', () => { + const spy = sinon.spy(); + + const ts = new TrailingStop({ + trail: 10, + initialPrice: 100, + onTrigger: spy + }); + + ts.updatePrice(50); + + expect(spy.called).to.be.true; + }); + + it('should emit a trigger event when the stop hits', () => { + const spy = sinon.spy(); + + const ts = new TrailingStop({ + trail: 10, + initialPrice: 100 + }); + + ts.on('trigger', spy); + + ts.updatePrice(50); + + expect(spy.called).to.be.true; + }); + + it('should not trigger when the the price does not go down', () => { + const spy = sinon.spy(); + + const ts = new TrailingStop({ + trail: 10, + initialPrice: 100, + onTrigger: spy + }); + + ts.updatePrice(100); + ts.updatePrice(101); + ts.updatePrice(102); + ts.updatePrice(103); + ts.updatePrice(104); + + expect(spy.called).to.be.false; + }); + + it('should not trigger when the the price goes down but above the offset', () => { + const spy = sinon.spy(); + + const ts = new TrailingStop({ + trail: 10, + initialPrice: 100, + onTrigger: spy + }); + + ts.updatePrice(99); + ts.updatePrice(98); + ts.updatePrice(97); + ts.updatePrice(96); + ts.updatePrice(95); + ts.updatePrice(94); + ts.updatePrice(93); + ts.updatePrice(92); + ts.updatePrice(91); + + expect(spy.called).to.be.false; + }); + + it('should trigger when the the price equals the offset', () => { + const spy = sinon.spy(); + + const ts = new TrailingStop({ + trail: 10, + initialPrice: 100, + onTrigger: spy + }); + + ts.updatePrice(99); + ts.updatePrice(98); + ts.updatePrice(92); + + expect(spy.called).to.be.false; + + ts.updatePrice(90); + + expect(spy.called).to.be.true; + }); + + it('should trigger when the the price goes up and down', () => { + const spy = sinon.spy(); + + const ts = new TrailingStop({ + trail: 10, + initialPrice: 100, + onTrigger: spy + }); + + ts.updatePrice(101); + ts.updatePrice(102); + ts.updatePrice(103); + ts.updatePrice(104); + ts.updatePrice(105); + + expect(spy.called).to.be.false; + + ts.updatePrice(95); + + expect(spy.called).to.be.true; + }); + + it('should only trigger once', () => { + const spy = sinon.spy(); + + const ts = new TrailingStop({ + trail: 10, + initialPrice: 100, + onTrigger: spy + }); + + ts.updatePrice(90); + expect(spy.called).to.be.true; + + const spy2 = sinon.spy(); + ts.on('trigger', spy2); + ts.updatePrice(80); + expect(spy2.called).to.be.false; + }); + + it('should not trigger when the the price swings above the trail', () => { + const spy = sinon.spy(); + + const ts = new TrailingStop({ + trail: 10, + initialPrice: 100, + onTrigger: spy + }); + + ts.updatePrice(110); + ts.updatePrice(101); + ts.updatePrice(111); + ts.updatePrice(102); + ts.updatePrice(112); + ts.updatePrice(103); + ts.updatePrice(113); + ts.updatePrice(104); + + expect(spy.called).to.be.false; + + ts.updatePrice(103); + + expect(spy.called).to.be.true; + }); + +}); \ No newline at end of file diff --git a/web/apiKeyManager.js b/web/apiKeyManager.js new file mode 100644 index 0000000..f378068 --- /dev/null +++ b/web/apiKeyManager.js @@ -0,0 +1,48 @@ +const fs = require('fs'); +const _ = require('lodash'); +const cache = require('./state/cache'); +const broadcast = cache.get('broadcast'); + +const apiKeysFile = __dirname + '/../SECRET-api-keys.json'; + +// on init: +const noApiKeysFile = !fs.existsSync(apiKeysFile); + +if(noApiKeysFile) + fs.writeFileSync( + apiKeysFile, + JSON.stringify({}) + ); + +const apiKeys = JSON.parse( fs.readFileSync(apiKeysFile, 'utf8') ); + +module.exports = { + get: () => _.keys(apiKeys), + + // note: overwrites if exists + add: (exchange, props) => { + apiKeys[exchange] = props; + fs.writeFileSync(apiKeysFile, JSON.stringify(apiKeys)); + + broadcast({ + type: 'apiKeys', + exchanges: _.keys(apiKeys) + }); + }, + remove: exchange => { + if(!apiKeys[exchange]) + return; + + delete apiKeys[exchange]; + fs.writeFileSync(apiKeysFile, JSON.stringify(apiKeys)); + + broadcast({ + type: 'apiKeys', + exchanges: _.keys(apiKeys) + }); + }, + + // retrieve api keys + // this cannot touch the frontend for security reaons. + _getApiKeyPair: key => apiKeys[key] +} \ No newline at end of file diff --git a/web/baseUIconfig.js b/web/baseUIconfig.js new file mode 100644 index 0000000..1acb922 --- /dev/null +++ b/web/baseUIconfig.js @@ -0,0 +1,26 @@ +// Note: this file gets copied around, make sure you edit +// the UIconfig located at `gekko/web/vue/dist/UIconfig.js`. + +// This config is used by both the frontend as well as the web server. +// see https://gekko.wizb.it/docs/installation/installing_gekko_on_a_server.html#Configuring-Gekko + +const CONFIG = { + headless: false, + api: { + host: '127.0.0.1', + port: 3000, + timeout: 120000 // 2 minutes + }, + ui: { + ssl: false, + host: 'localhost', + port: 3000, + path: '/' + }, + adapter: 'sqlite' +} + +if(typeof window === 'undefined') + module.exports = CONFIG; +else + window.CONFIG = CONFIG; diff --git a/web/isWindows.js b/web/isWindows.js new file mode 100644 index 0000000..691eb3d --- /dev/null +++ b/web/isWindows.js @@ -0,0 +1,9 @@ +const os = require('os'); + +var isWindows = ( + os.platform() == 'win32' // true evenon 64 bit archs + || os.release().indexOf('Microsoft') > -1 // bash on Windows 10 scenario +); + +module.exports = isWindows; + diff --git a/web/routes/apiKeys.js b/web/routes/apiKeys.js new file mode 100644 index 0000000..c2c702b --- /dev/null +++ b/web/routes/apiKeys.js @@ -0,0 +1,26 @@ +const cache = require('../state/cache'); +const manager = cache.get('apiKeyManager'); + +module.exports = { + get: function *() { + this.body = manager.get(); + }, + add: function *() { + const content = this.request.body; + + manager.add(content.exchange, content.values); + + this.body = { + status: 'ok' + }; + }, + remove: function *() { + const exchange = this.request.body.exchange; + + manager.remove(exchange); + + this.body = { + status: 'ok' + }; + } +} \ No newline at end of file diff --git a/web/routes/backtest.js b/web/routes/backtest.js new file mode 100644 index 0000000..4451041 --- /dev/null +++ b/web/routes/backtest.js @@ -0,0 +1,31 @@ +// simple POST request that returns the backtest result + +const _ = require('lodash'); +const promisify = require('tiny-promisify'); +const pipelineRunner = promisify(require('../../core/workers/pipeline/parent')); + +// starts a backtest +// requires a post body like: +// +// { +// gekkoConfig: {watch: {exchange: "poloniex", currency: "USDT", asset: "BTC"},…},…} +// data: { +// candleProps: ["close", "start"], +// indicatorResults: true, +// report: true, +// roundtrips: true +// } +// } +module.exports = function *() { + var mode = 'backtest'; + + var config = {}; + + var base = require('./baseConfig'); + + var req = this.request.body; + + _.merge(config, base, req); + + this.body = yield pipelineRunner(mode, config); +} \ No newline at end of file diff --git a/web/routes/baseConfig.js b/web/routes/baseConfig.js new file mode 100644 index 0000000..04be0d8 --- /dev/null +++ b/web/routes/baseConfig.js @@ -0,0 +1,104 @@ +var UIconfig = require('../vue/dist/UIconfig'); + +var config = {}; + +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// GENERAL SETTINGS +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +config.silent = false; +config.debug = true; + +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// CONFIGURING TRADING ADVICE +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +config.tradingAdvisor = { +} + +config.candleWriter = { + enabled: false +} + +config.backtestResultExporter = { + enabled: false, + writeToDisk: false, + data: { + stratUpdates: false, + roundtrips: true, + stratCandles: true, + trades: true + } +} + +config.childToParent = { + enabled: false, +} + +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// CONFIGURING ADAPTER +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +// configurable in the UIconfig +config.adapter = UIconfig.adapter; + +config.sqlite = { + path: 'plugins/sqlite', + version: 0.1, + dataDirectory: 'history', + journalMode: require('../isWindows.js') ? 'PERSIST' : 'WAL', + dependencies: [{ + module: 'sqlite3', + version: '3.1.4' + }] +} + + // Postgres adapter example config (please note: requires postgres >= 9.5): +config.postgresql = { + path: 'plugins/postgresql', + version: 0.1, + connectionString: 'postgres://user:pass@localhost:5432', // if default port + database: null, // if set, we'll put all tables into a single database. + schema: 'public', + dependencies: [{ + module: 'pg', + version: '7.4.3' + }] +} + +// Mongodb adapter, requires mongodb >= 3.3 (no version earlier tested) +config.mongodb = { + path: 'plugins/mongodb', + version: 0.1, + connectionString: 'mongodb://mongodb/gekko', // connection to mongodb server + dependencies: [{ + module: 'mongojs', + version: '2.4.0' + }] +} + +config.adviceWriter = { + enabled: false, + muteSoft: true, +} + +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// CONFIGURING BACKTESTING +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +// Note that these settings are only used in backtesting mode, see here: +// @link: https://github.com/askmike/gekko/blob/stable/docs/Backtesting.md + +config.backtest = { + daterange: 'scan', + batchSize: 50 +} + +config.importer = { + daterange: { + // NOTE: these dates are in UTC + from: "2016-06-01 12:00:00" + } +} + +module.exports = config; diff --git a/web/routes/configPart.js b/web/routes/configPart.js new file mode 100644 index 0000000..203e454 --- /dev/null +++ b/web/routes/configPart.js @@ -0,0 +1,20 @@ +const _ = require('lodash'); +const fs = require('co-fs'); + +const parts = { + paperTrader: 'config/plugins/paperTrader', + candleWriter: 'config/plugins/candleWriter', + performanceAnalyzer: 'config/plugins/performanceAnalyzer' +} + +const gekkoRoot = __dirname + '/../../'; + +module.exports = function *() { + if(!_.has(parts, this.params.part)) + return this.body = 'error :('; + + const fileName = gekkoRoot + '/' + parts[this.params.part] + '.toml'; + this.body = { + part: yield fs.readFile(fileName, 'utf8') + } +} \ No newline at end of file diff --git a/web/routes/deleteGekko.js b/web/routes/deleteGekko.js new file mode 100644 index 0000000..ee687e6 --- /dev/null +++ b/web/routes/deleteGekko.js @@ -0,0 +1,22 @@ +const cache = require('../state/cache'); +const gekkoManager = cache.get('gekkos'); + +// Deletes a gekko +// requires a post body with an id +module.exports = function *() { + + let id = this.request.body.id; + + if(!id) { + this.body = { status: 'not ok' } + return; + } + + try { + gekkoManager.delete(id); + } catch(e) { + this.body = { status: e.message } + } + + this.body = { status: 'ok' }; +} \ No newline at end of file diff --git a/web/routes/exchanges.js b/web/routes/exchanges.js new file mode 100644 index 0000000..8fe4367 --- /dev/null +++ b/web/routes/exchanges.js @@ -0,0 +1,39 @@ +const _ = require('lodash'); +const fs = require('co-fs'); + +const gekkoRoot = __dirname + '/../../'; +var util = require(__dirname + '/../../core/util'); + +var config = {}; + +config.debug = false; +config.silent = false; + +util.setConfig(config); + +module.exports = function *() { + const exchangesDir = yield fs.readdir(gekkoRoot + 'exchange/wrappers/'); + const exchanges = exchangesDir + .filter(f => _.last(f, 3).join('') === '.js') + .map(f => f.slice(0, -3)); + + let allCapabilities = []; + + exchanges.forEach(function (exchange) { + let Trader = null; + + try { + Trader = require(gekkoRoot + 'exchange/wrappers/' + exchange); + } catch (e) { + return; + } + + if (!Trader || !Trader.getCapabilities) { + return; + } + + allCapabilities.push(Trader.getCapabilities()); + }); + + this.body = allCapabilities; +} \ No newline at end of file diff --git a/web/routes/getCandles.js b/web/routes/getCandles.js new file mode 100644 index 0000000..4d74435 --- /dev/null +++ b/web/routes/getCandles.js @@ -0,0 +1,40 @@ +// simple POST request that returns the candles requested + +// expects a config like: + +// let config = { +// watch: { +// exchange: 'poloniex', +// currency: 'USDT', +// asset: 'BTC' +// }, +// daterange: { +// from: '2016-05-22 11:22', +// to: '2016-06-03 19:56' +// }, +// adapter: 'sqlite', +// sqlite: { +// path: 'plugins/sqlite', + +// dataDirectory: 'history', +// version: 0.1, + +// dependencies: [{ +// module: 'sqlite3', +// version: '3.1.4' +// }] +// }, +// candleSize: 100 +// } + +const _ = require('lodash'); +const promisify = require('tiny-promisify'); +const candleLoader = promisify(require('../../core/workers/loadCandles/parent')); +const base = require('./baseConfig'); + +module.exports = function *() { + + config = {}; + _.merge(config, base, this.request.body); + this.body = yield candleLoader(config); +} \ No newline at end of file diff --git a/web/routes/import.js b/web/routes/import.js new file mode 100644 index 0000000..e627948 --- /dev/null +++ b/web/routes/import.js @@ -0,0 +1,75 @@ +const _ = require('lodash'); +const promisify = require('tiny-promisify'); +const pipelineRunner = promisify(require('../../core/workers/pipeline/parent')); + +const cache = require('../state/cache'); +const broadcast = cache.get('broadcast'); +const importManager = cache.get('imports'); + +const base = require('./baseConfig'); + +// starts an import +// requires a post body with a config object +module.exports = function *() { + let mode = 'importer'; + + let config = {} + + _.merge(config, base, this.request.body); + + let importId = (Math.random() + '').slice(3); + + let errored = false; + + console.log('Import', importId, 'started'); + + pipelineRunner(mode, config, (err, event) => { + if(errored) + return; + + if(err) { + errored = true; + console.error('RECEIVED ERROR IN IMPORT', importId); + console.error(err); + importManager.delete(importId); + return broadcast({ + type: 'import_error', + import_id: importId, + error: err + }); + } + + if(!event) + return; + + // update local cache + importManager.update(importId, { + latest: event.latest, + done: event.done + }); + + // emit update over ws + let wsEvent = { + type: 'import_update', + import_id: importId, + updates: { + latest: event.latest, + done: event.done + } + } + broadcast(wsEvent); + }); + + let daterange = this.request.body.importer.daterange; + + const _import = { + watch: config.watch, + id: importId, + latest: '', + from: daterange.from, + to: daterange.to + } + + importManager.add(_import); + this.body = _import; +} \ No newline at end of file diff --git a/web/routes/info.js b/web/routes/info.js new file mode 100644 index 0000000..a8969ad --- /dev/null +++ b/web/routes/info.js @@ -0,0 +1,8 @@ +const p = require('../../package.json'); + +// Retrieves API information +module.exports = function *() { + this.body = { + version: p.version + } +} \ No newline at end of file diff --git a/web/routes/list.js b/web/routes/list.js new file mode 100644 index 0000000..3e76b88 --- /dev/null +++ b/web/routes/list.js @@ -0,0 +1,7 @@ +const cache = require('../state/cache'); + +module.exports = function(name) { + return function *() { + this.body = cache.get(name).list(); + } +} \ No newline at end of file diff --git a/web/routes/scanDatasets.js b/web/routes/scanDatasets.js new file mode 100644 index 0000000..c15a04a --- /dev/null +++ b/web/routes/scanDatasets.js @@ -0,0 +1,19 @@ +const _ = require('lodash'); +const promisify = require('promisify-node'); + +const scan = promisify(require('../../core/workers/datasetScan/parent')); + +// starts a scan +// requires a post body with configuration of: +// +// - config.watch +const route = function *() { + + var config = require('./baseConfig'); + + _.merge(config, this.request.body); + + this.body = yield scan(config); +}; + +module.exports = route; \ No newline at end of file diff --git a/web/routes/scanDateRange.js b/web/routes/scanDateRange.js new file mode 100644 index 0000000..8d369ef --- /dev/null +++ b/web/routes/scanDateRange.js @@ -0,0 +1,19 @@ +const _ = require('lodash'); +const promisify = require('promisify-node'); + +const scan = promisify(require('../../core/workers/dateRangeScan/parent')); + +// starts a scan +// requires a post body with configuration of: +// +// - config.watch +const route = function *() { + + var config = require('./baseConfig'); + + _.merge(config, this.request.body); + + this.body = yield scan(config); +}; + +module.exports = route; \ No newline at end of file diff --git a/web/routes/startGekko.js b/web/routes/startGekko.js new file mode 100644 index 0000000..8d28ed6 --- /dev/null +++ b/web/routes/startGekko.js @@ -0,0 +1,38 @@ +const _ = require('lodash'); + +const cache = require('../state/cache'); +const Logger = require('../state/logger'); +const apiKeyManager= cache.get('apiKeyManager'); +const gekkoManager = cache.get('gekkos'); + +const base = require('./baseConfig'); + +// starts an import +// requires a post body with a config object +module.exports = function *() { + const mode = this.request.body.mode; + + let config = {}; + + _.merge(config, base, this.request.body); + + // Attach API keys + if(config.trader && config.trader.enabled && !config.trader.key) { + + const keys = apiKeyManager._getApiKeyPair(config.watch.exchange); + + if(!keys) { + this.body = 'No API keys found for this exchange.'; + return; + } + + _.merge( + config.trader, + keys + ); + } + + const state = gekkoManager.add({config, mode}); + + this.body = state; +} diff --git a/web/routes/stopGekko.js b/web/routes/stopGekko.js new file mode 100644 index 0000000..031d7f9 --- /dev/null +++ b/web/routes/stopGekko.js @@ -0,0 +1,23 @@ +const cache = require('../state/cache'); +const gekkoManager = cache.get('gekkos'); + +// stops a Gekko +// requires a post body with an id +module.exports = function *() { + + let id = this.request.body.id; + + if(!id) { + this.body = { status: 'not ok' } + return; + } + + let stopped = gekkoManager.stop(id); + + if(!stopped) { + this.body = { status: 'not ok' } + return; + } + + this.body = { status: 'ok' }; +} \ No newline at end of file diff --git a/web/routes/strategies.js b/web/routes/strategies.js new file mode 100644 index 0000000..69749cc --- /dev/null +++ b/web/routes/strategies.js @@ -0,0 +1,27 @@ +const _ = require('lodash'); +const fs = require('co-fs'); + +const gekkoRoot = __dirname + '/../../'; + +module.exports = function *() { + const strategyDir = yield fs.readdir(gekkoRoot + 'strategies'); + const strats = strategyDir + .filter(f => _.last(f, 3).join('') === '.js') + .map(f => { + return { name: f.slice(0, -3) } + }); + + // for every strat, check if there is a config file and add it + const stratConfigPath = gekkoRoot + 'config/strategies'; + const strategyParamsDir = yield fs.readdir(stratConfigPath); + + for(let i = 0; i < strats.length; i++) { + let strat = strats[i]; + if(strategyParamsDir.indexOf(strat.name + '.toml') !== -1) + strat.params = yield fs.readFile(stratConfigPath + '/' + strat.name + '.toml', 'utf8') + else + strat.params = ''; + } + + this.body = strats; +} \ No newline at end of file diff --git a/web/server.js b/web/server.js new file mode 100644 index 0000000..140b3e1 --- /dev/null +++ b/web/server.js @@ -0,0 +1,139 @@ +const config = require('./vue/dist/UIconfig'); + +const koa = require('koa'); +const serve = require('koa-static'); +const cors = require('koa-cors'); +const _ = require('lodash'); +const bodyParser = require('koa-bodyparser'); + +const opn = require('opn'); +const server = require('http').createServer(); +const router = require('koa-router')(); +const ws = require('ws'); +const app = koa(); + +const WebSocketServer = require('ws').Server; +const wss = new WebSocketServer({ server: server }); + +const cache = require('./state/cache'); + +const nodeCommand = _.last(process.argv[1].split('/')); +const isDevServer = nodeCommand === 'server' || nodeCommand === 'server.js'; + +wss.on('connection', ws => { + ws.isAlive = true; + ws.on('pong', () => { + ws.isAlive = true; + }); + ws.ping(_.noop); + ws.on('error', e => { + console.error(new Date, '[WS] connection error:', e); + }); +}); + + +setInterval(() => { + wss.clients.forEach(ws => { + if(!ws.isAlive) { + console.log(new Date, '[WS] stale websocket client, terminiating..'); + return ws.terminate(); + } + + ws.isAlive = false; + ws.ping(_.noop); + }); +}, 10 * 1000); + +// broadcast function +const broadcast = data => { + if(_.isEmpty(data)) { + return; + } + + const payload = JSON.stringify(data); + + wss.clients.forEach(ws => { + ws.send(payload, err => { + if(err) { + console.log(new Date, '[WS] unable to send data to client:', err); + } + }); + } + ); +} +cache.set('broadcast', broadcast); + + +const ListManager = require('./state/listManager'); +const GekkoManager = require('./state/gekkoManager'); + +// initialize lists and dump into cache +cache.set('imports', new ListManager); +cache.set('gekkos', new GekkoManager); +cache.set('apiKeyManager', require('./apiKeyManager')); + +// setup API routes + +const WEBROOT = __dirname + '/'; +const ROUTE = n => WEBROOT + 'routes/' + n; + +// attach routes +const apiKeys = require(ROUTE('apiKeys')); +router.get('/api/info', require(ROUTE('info'))); +router.get('/api/strategies', require(ROUTE('strategies'))); +router.get('/api/configPart/:part', require(ROUTE('configPart'))); +router.get('/api/apiKeys', apiKeys.get); + +const listWraper = require(ROUTE('list')); +router.get('/api/imports', listWraper('imports')); +router.get('/api/gekkos', listWraper('gekkos')); +router.get('/api/exchanges', require(ROUTE('exchanges'))); + +router.post('/api/addApiKey', apiKeys.add); +router.post('/api/removeApiKey', apiKeys.remove); +router.post('/api/scan', require(ROUTE('scanDateRange'))); +router.post('/api/scansets', require(ROUTE('scanDatasets'))); +router.post('/api/backtest', require(ROUTE('backtest'))); +router.post('/api/import', require(ROUTE('import'))); +router.post('/api/startGekko', require(ROUTE('startGekko'))); +router.post('/api/stopGekko', require(ROUTE('stopGekko'))); +router.post('/api/deleteGekko', require(ROUTE('deleteGekko'))); +router.post('/api/getCandles', require(ROUTE('getCandles'))); + + +// incoming WS: +// wss.on('connection', ws => { +// ws.on('message', _.noop); +// }); + +app + .use(cors()) + .use(serve(WEBROOT + 'vue/dist')) + .use(bodyParser()) + .use(require('koa-logger')()) + .use(router.routes()) + .use(router.allowedMethods()); + +server.timeout = config.api.timeout || 120000; +server.on('request', app.callback()); +server.listen(config.api.port, config.api.host, '::', () => { + const host = `${config.ui.host}:${config.ui.port}${config.ui.path}`; + + if(config.ui.ssl) { + var location = `https://${host}`; + } else { + var location = `http://${host}`; + } + + console.log('Serving Gekko UI on ' + location + '\n'); + + + // only open a browser when running `node gekko` + // this prevents opening the browser during development + if(!isDevServer && !config.headless) { + opn(location) + .catch(err => { + console.log('Something went wrong when trying to open your web browser. UI is running on ' + location + '.'); + }); + } +}); diff --git a/web/state/cache.js b/web/state/cache.js new file mode 100644 index 0000000..0921051 --- /dev/null +++ b/web/state/cache.js @@ -0,0 +1,14 @@ +const _ = require('lodash'); + +const cache = {}; + +module.exports = { + set: (name, val) => { + cache[name] = val; + return true; + }, + get: name => { + if(_.has(cache, name)) + return cache[name]; + } +} \ No newline at end of file diff --git a/web/state/gekkoManager.js b/web/state/gekkoManager.js new file mode 100644 index 0000000..0d7b89e --- /dev/null +++ b/web/state/gekkoManager.js @@ -0,0 +1,254 @@ +const _ = require('lodash'); +const moment = require('moment'); + +const broadcast = require('./cache').get('broadcast'); +const Logger = require('./logger'); +const pipelineRunner = require('../../core/workers/pipeline/parent'); +const reduceState = require('./reduceState.js'); +const now = () => moment().format('YYYY-MM-DD HH:mm'); + +const GekkoManager = function() { + this.gekkos = {}; + this.instances = {}; + this.loggers = {}; + + this.archivedGekkos = {}; +} + +GekkoManager.prototype.add = function({mode, config}) { + // set type + let type; + if(mode === 'realtime') { + if(config.market && config.market.type) + type = config.market.type; + else + type = 'watcher'; + } else { + type = ''; + } + + let logType = type; + if(logType === 'leech') { + if(config.trader && config.trader.enabled) + logType = 'tradebot'; + else + logType = 'papertrader'; + } + + const date = now().replace(' ', '-').replace(':', '-'); + const n = (Math.random() + '').slice(3); + const id = `${date}-${logType}-${n}`; + + // make sure we catch events happening inside te gekko instance + config.childToParent.enabled = true; + + const state = { + mode, + config, + id, + type, + logType, + active: true, + stopped: false, + errored: false, + errorMessage: false, + events: { + initial: {}, + latest: {} + }, + start: moment() + } + + this.gekkos[id] = state; + + this.loggers[id] = new Logger(id); + + // start the actual instance + this.instances[id] = pipelineRunner(mode, config, this.handleRawEvent(id)); + + // after passing API credentials to the actual instance we mask them + if(logType === 'trader') { + config.trader.key = '[REDACTED]'; + config.trader.secret = '[REDACTED]'; + } + + console.log(`${now()} Gekko ${id} started.`); + + broadcast({ + type: 'gekko_new', + id, + state + }); + + return state; +} + +GekkoManager.prototype.handleRawEvent = function(id) { + const logger = this.loggers[id]; + + return (err, event) => { + if(err) { + return this.handleFatalError(id, err); + } + + if(!event) { + return; + } + + if(event.log) { + return logger.write(event.message); + } + + if(event.type) { + this.handleGekkoEvent(id, event); + } + } +} + +GekkoManager.prototype.handleGekkoEvent = function(id, event) { + this.gekkos[id] = reduceState(this.gekkos[id], event); + broadcast({ + type: 'gekko_event', + id, + event + }); +} + +GekkoManager.prototype.handleFatalError = function(id, err) { + const state = this.gekkos[id]; + + if(!state || state.errored || state.stopped) + return; + + state.errored = true; + state.errorMessage = err; + console.error('RECEIVED ERROR IN GEKKO INSTANCE', id); + console.error(err); + broadcast({ + type: 'gekko_error', + id, + error: err + }); + + this.archive(id); + + if(state.logType === 'watcher') { + this.handleWatcherError(state, id); + } +} + +// There might be leechers depending on this watcher, if so +// figure out it we can safely start a new watcher without +// the leechers noticing. +GekkoManager.prototype.handleWatcherError = function(state, id) { + console.log(`${now()} A gekko watcher crashed.`); + if(!state.events.latest.candle) { + console.log(`${now()} was unable to start.`); + } + + let latestCandleTime = moment.unix(0); + if(state.events.latest && state.events.latest.candle) { + latestCandleTime = state.events.latest.candle.start; + } + const leechers = _.values(this.gekkos) + .filter(gekko => { + if(gekko.type !== 'leech') { + return false; + } + + if(_.isEqual(gekko.config.watch, state.config.watch)) { + return true; + } + }); + + if(leechers.length) { + console.log(`${now()} ${leechers.length} leecher(s) were depending on this watcher.`); + if(moment().diff(latestCandleTime, 'm') < 60) { + console.log(`${now()} Watcher had recent data, starting a new one in a minute.`); + // after a minute try to start a new one again.. + setTimeout(() => { + const mode = 'realtime'; + const config = state.config; + this.add({mode, config}); + }, 1000 * 60); + } else { + console.log(`${now()} Watcher did not have recent data, killing its leechers.`); + leechers.forEach(leecher => this.stop(leecher.id)); + } + + } +} + +GekkoManager.prototype.stop = function(id) { + if(!this.gekkos[id]) + return false; + + console.log(`${now()} stopping Gekko ${id}`); + + this.gekkos[id].stopped = true; + this.gekkos[id].active = false; + + // todo: graceful shutdown (via gekkoStream's + // finish function). + this.instances[id].kill(); + + broadcast({ + type: 'gekko_stopped', + id + }); + + this.archive(id); + + return true; +} + +GekkoManager.prototype.archive = function(id) { + this.archivedGekkos[id] = this.gekkos[id]; + this.archivedGekkos[id].stopped = true; + this.archivedGekkos[id].active = false; + delete this.gekkos[id]; + + broadcast({ + type: 'gekko_archived', + id + }); +} + +GekkoManager.prototype.delete = function(id) { + if(this.gekkos[id]) { + throw new Error('Cannot delete a running Gekko, stop it first.'); + } + + if(!this.archivedGekkos[id]) { + throw new Error('Cannot delete unknown Gekko.'); + } + + console.log(`${now()} deleting Gekko ${id}`); + + broadcast({ + type: 'gekko_deleted', + id + }); + + delete this.archivedGekkos[id]; + + return true; +} + +GekkoManager.prototype.archive = function(id) { + this.archivedGekkos[id] = this.gekkos[id]; + this.archivedGekkos[id].stopped = true; + this.archivedGekkos[id].active = false; + delete this.gekkos[id]; + + broadcast({ + type: 'gekko_archived', + id + }); +} + +GekkoManager.prototype.list = function() { + return { live: this.gekkos, archive: this.archivedGekkos }; +} + +module.exports = GekkoManager; \ No newline at end of file diff --git a/web/state/listManager.js b/web/state/listManager.js new file mode 100644 index 0000000..3486f31 --- /dev/null +++ b/web/state/listManager.js @@ -0,0 +1,59 @@ +// manages a list of things that change over time +// used for: +// - The currently running imports +// - etc.. +const _ = require('lodash'); + +var ListManager = function() { + this._list = []; +} + +// add an item to the list +ListManager.prototype.add = function(obj) { + if(!obj.id) + return false; + this._list.push(_.clone(obj)); + return true; +} + +// update some properties on an item +ListManager.prototype.update = function(id, updates) { + let item = this._list.find(i => i.id === id); + if(!item) + return false; + _.merge(item, updates); + return true; +} + +// get an item from the list +ListManager.prototype.get = function(id) { + return this._list.find(i => i.id === id); +} + +// push a value to a array property of an item +ListManager.prototype.push = function(id, prop, value) { + let item = this._list.find(i => i.id === id); + if(!item) + return false; + + item[prop].push(value); + return true; +} + +// delete an item from the list +ListManager.prototype.delete = function(id) { + let wasThere = this._list.find(i => i.id === id); + this._list = this._list.filter(i => i.id !== id); + + if(wasThere) + return true; + else + return false; +} + +// getter +ListManager.prototype.list = function() { + return this._list; +} + +module.exports = ListManager; \ No newline at end of file diff --git a/web/state/logger.js b/web/state/logger.js new file mode 100644 index 0000000..dd5d631 --- /dev/null +++ b/web/state/logger.js @@ -0,0 +1,38 @@ +const fs = require('fs'); +const _ = require('lodash'); + +const BASEPATH = __dirname + '/../../logs/'; + +const Logger = function(id) { + + this.fileName = `${id}.log`; + + this.writing = false; + this.queue = []; + + _.bindAll(this); +} + +Logger.prototype.write = function(line) { + if(!this.writing) { + this.writing = true; + fs.appendFile( + BASEPATH + this.fileName, + line + '\n', + this.handleWriteCallback + ); + } else + this.queue.push(line); +} + +Logger.prototype.handleWriteCallback = function(err) { + if(err) + console.error(`ERROR WRITING LOG FILE ${this.fileName}:`, err); + + this.writing = false; + + if(_.size(this.queue)) + this.write(this.queue.shift()) +} + +module.exports = Logger; \ No newline at end of file diff --git a/web/state/reduceState.js b/web/state/reduceState.js new file mode 100644 index 0000000..160b087 --- /dev/null +++ b/web/state/reduceState.js @@ -0,0 +1,67 @@ +// Redux/vuex inspired reducer, reduces an event into a gekko state. +// NOTE: this is used by the backend as well as the frontend. + +const skipInitialEvents = ['marketUpdate']; +const skipLatestEvents = ['marketStart', 'stratWarmupCompleted']; +const trackAllEvents = ['tradeCompleted', 'advice', 'roundtrip']; + +const reduce = (state, event) => { + const type = event.type; + const payload = event.payload; + + state = { + ...state, + latestUpdate: new Date() + } + + if(trackAllEvents.includes(type)) { + if(!state.events[type]) { + state = { + ...state, + events: { + ...state.events, + [type]: [ payload ] + } + } + } else { + state = { + ...state, + events: { + ...state.events, + [type]: [ ...state.events[type], payload ] + } + } + } + } + + if(!state.events.initial[type] && !skipInitialEvents.includes(type)) { + state = { + ...state, + events: { + ...state.events, + initial: { + ...state.events.initial, + [type]: payload + } + } + } + } + + if(!skipLatestEvents.includes(type)) { + state = { + ...state, + events: { + ...state.events, + latest: { + ...state.events.latest, + [type]: payload + } + } + } + } + + return state; +} + +// export default reduce; +module.exports = reduce; \ No newline at end of file diff --git a/web/vue/babel.config.js b/web/vue/babel.config.js new file mode 100644 index 0000000..e6d0107 --- /dev/null +++ b/web/vue/babel.config.js @@ -0,0 +1,9 @@ +module.exports = { + presets: [ + '@vue/app' + ], + ignore: [ + 'node_modules', + ], + plugins : ["transform-commonjs-es2015-modules"] +} diff --git a/web/vue/dist/UIconfig.js b/web/vue/dist/UIconfig.js new file mode 100644 index 0000000..1acb922 --- /dev/null +++ b/web/vue/dist/UIconfig.js @@ -0,0 +1,26 @@ +// Note: this file gets copied around, make sure you edit +// the UIconfig located at `gekko/web/vue/dist/UIconfig.js`. + +// This config is used by both the frontend as well as the web server. +// see https://gekko.wizb.it/docs/installation/installing_gekko_on_a_server.html#Configuring-Gekko + +const CONFIG = { + headless: false, + api: { + host: '127.0.0.1', + port: 3000, + timeout: 120000 // 2 minutes + }, + ui: { + ssl: false, + host: 'localhost', + port: 3000, + path: '/' + }, + adapter: 'sqlite' +} + +if(typeof window === 'undefined') + module.exports = CONFIG; +else + window.CONFIG = CONFIG; diff --git a/web/vue/dist/app.5e99ecf7.js b/web/vue/dist/app.5e99ecf7.js new file mode 100644 index 0000000..cd1e04b --- /dev/null +++ b/web/vue/dist/app.5e99ecf7.js @@ -0,0 +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=a.n(n),i=r.a,s=new i.Renderer;s.link=function(t,e,a){var n,r,i;return n=/^https?:\/\/.+$/.test(t),r=n||"newWindow"===e,i='"+a+""},i.setOptions({renderer:s}),e["a"]=i},"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.8",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:{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",relieve:"^2.1.3",retry:"^0.10.1",semver:"5.4.1",sqlite3:"^4.0.4","stats-lite":"^2.0.4","tiny-promisify":"^0.1.1",toml:"^2.3.0",ws:"^6.0.0"},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("kiQV"),v=a("9RND"),p=v,g=m,_={data:function(){return{version:{gekko:p.version,ui:g.version}}}},k=_,y=Object(u["a"])(k,f,h,!1,null,null,null),w=y.exports,b=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()},C=[],x=a("5shn"),S={disconnected:Object(x["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 ")},T={computed:{active:function(){return!this.$store.state.warnings.connected},content:function(){return this.$store.state.warnings.connected?"":S.disconnected}}},E=T,P=(a("TfKe"),Object(u["a"])(E,b,C,!1,null,null,null)),A=P.exports,j={name:"app",components:{top:d,bottom:w,modal:A}},D=j,O=(a("ZL7j"),Object(u["a"])(D,n,r,!1,null,null,null));e["a"]=O.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 C}),a.d(r,"addGekko",function(){return x}),a.d(r,"updateGekko",function(){return S}),a.d(r,"archiveGekko",function(){return T}),a.d(r,"errorGekko",function(){return E}),a.d(r,"deleteGekko",function(){return P});var i={};a.r(i),a.d(i,"setGlobalWarning",function(){return A});var s={};a.r(s),a.d(s,"syncApiKeys",function(){return j}),a.d(s,"syncExchanges",function(){return D});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("iv4g"),g=(a("Z2Ku"),["marketUpdate"]),_=["marketStart","stratWarmupCompleted"],k=["tradeCompleted","advice","roundtrip"],y=function(t,e){var a=e.type,n=e.payload;return t=Object(v["a"])({},t,{latestUpdate:new Date}),k.includes(a)&&(t=t.events[a]?Object(v["a"])({},t,{events:Object(v["a"])({},t.events,Object(m["a"])({},a,Object(p["a"])(t.events[a]).concat([n])))}):Object(v["a"])({},t,{events:Object(v["a"])({},t.events,Object(m["a"])({},a,[n]))})),t.events.initial[a]||g.includes(a)||(t=Object(v["a"])({},t,{events:Object(v["a"])({},t.events,{initial:Object(v["a"])({},t.events.initial,Object(m["a"])({},a,n))})})),_.includes(a)||(t=Object(v["a"])({},t,{events:Object(v["a"])({},t.events,{latest:Object(v["a"])({},t.events.latest,Object(m["a"])({},a,n))})})),t},w=y,b=w,C=function(t,e){return e?(t.gekkos=e.live,t.archivedGekkos=e.archive,t):t},x=function(t,e){return t.gekkos=Object(v["a"])({},t.gekkos,Object(m["a"])({},e.id,e)),t},S=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,b(t.gekkos[e.id],e.event))),t):console.error("cannot update unknown gekko..")},T=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..")},E=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..")},P=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..")},A=function(t,e){return t.warnings[e.key]=e.value,t},j=function(t,e){return o["a"].set(t,"apiKeys",e),t},D=function(t,e){return o["a"].set(t,"exchanges",e),t};o["a"].use(c["a"]);var O=!1,z={};l.a.merge(z,n),l.a.merge(z,r),l.a.merge(z,i),l.a.merge(z,s);e["a"]=new c["a"].Store({state:{warnings:{connected:!0},imports:[],gekkos:{},archivedGekkos:{},connection:{disconnected:!1,reconnected:!1},apiKeys:[],exchanges:{}},mutations:z,strict:O})},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",j),x=d3.zoom().scaleExtent([1,100]).translateExtent([[0,0],[m,f]]).extent([[0,0],[m,f]]).on("zoom",O),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 j(){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 O(){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"}}},O=D,z=(a("WlXR"),Object(g["a"])(O,P,A,!1,null,null,null)),$=z.exports,M=a("UeuA"),G=a("vf3E"),I={props:["result"],data:function(){return{}},methods:{},components:{roundtripTable:G["a"],resultSummary:$,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},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"),{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("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("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.5e99ecf7.js.map \ No newline at end of file diff --git a/web/vue/dist/app.5e99ecf7.js.map b/web/vue/dist/app.5e99ecf7.js.map new file mode 100644 index 0000000..993b01c --- /dev/null +++ b/web/vue/dist/app.5e99ecf7.js.map @@ -0,0 +1 @@ +{"version":3,"sources":["webpack:///webpack/bootstrap","webpack:///./src/components/gekko/singleGekko.vue?6cec","webpack:///./src/components/global/progressBar.vue?7bcc","webpack:///./src/components/global/paperTradeSummary.vue?13b5","webpack:///src/components/global/paperTradeSummary.vue","webpack:///./src/components/global/paperTradeSummary.vue?a8ef","webpack:///./src/components/global/paperTradeSummary.vue","webpack:///./src/components/global/configbuilder/papertrader.vue?c5cd","webpack:///./src/components/gekko/new.vue?c104","webpack:///./src/components/gekko/new.vue?367c","webpack:///./src/components/gekko/gekkoConfigBuilder.vue?035a","webpack:///./src/components/global/configbuilder/typepicker.vue?df24","webpack:///src/components/global/configbuilder/typepicker.vue","webpack:///./src/components/global/configbuilder/typepicker.vue?cf28","webpack:///./src/components/global/configbuilder/typepicker.vue","webpack:///src/components/gekko/gekkoConfigBuilder.vue","webpack:///./src/components/gekko/gekkoConfigBuilder.vue?8d92","webpack:///./src/components/gekko/gekkoConfigBuilder.vue","webpack:///src/components/gekko/new.vue","webpack:///./src/components/gekko/new.vue?ae9a","webpack:///./src/components/gekko/new.vue","webpack:///./src/components/data/data.vue?4abe","webpack:///./src/tools/marked.js","webpack:///./src/components/global/configbuilder/marketpicker.vue?d701","webpack:///src/components/global/configbuilder/marketpicker.vue","webpack:///./src/components/global/configbuilder/marketpicker.vue?200d","webpack:///./src/components/global/configbuilder/marketpicker.vue","webpack:///./src/components/global/configbuilder/stratpicker.vue?fda9","webpack:///src/components/global/configbuilder/stratpicker.vue","webpack:///./src/components/global/configbuilder/stratpicker.vue?8a34","webpack:///./src/components/global/configbuilder/stratpicker.vue","webpack:///./src/components/global/configbuilder/rangepicker.vue?ce7c","webpack:///./src/components/global/configbuilder/rangecreator.vue?8425","webpack:///src/components/global/configbuilder/rangecreator.vue","webpack:///./src/components/global/configbuilder/rangecreator.vue?3f8d","webpack:///./src/components/global/configbuilder/rangecreator.vue","webpack:///./src/components/data/import/importConfigBuilder.vue?3911","webpack:///./src/components/global/blockSpinner.vue?cca6","webpack:///src/components/global/blockSpinner.vue","webpack:///./src/components/global/blockSpinner.vue?7b17","webpack:///./src/components/global/blockSpinner.vue","webpack:///./src/App.vue?77b6","webpack:///./src/components/layout/header.vue?345a","webpack:///src/components/layout/header.vue","webpack:///./src/components/layout/header.vue?8ecd","webpack:///./src/components/layout/header.vue","webpack:///./src/components/layout/footer.vue?0614","webpack:///src/components/layout/footer.vue","webpack:///./src/components/layout/footer.vue?6aff","webpack:///./src/components/layout/footer.vue","webpack:///./src/components/layout/modal.vue?5ed7","webpack:///src/components/layout/modal.vue","webpack:///./src/components/layout/modal.vue?6e33","webpack:///./src/components/layout/modal.vue","webpack:///src/App.vue","webpack:///./src/App.vue?8f16","webpack:///./src/App.vue","webpack:///./src/store/modules/imports/mutations.js","webpack:///../state/reduceState.js","webpack:///./src/store/modules/gekkos/mutations.js","webpack:///./src/store/modules/notifications/mutations.js","webpack:///./src/store/modules/config/mutations.js","webpack:///./src/store/index.js","webpack:///./src/components/backtester/backtestConfigBuilder.vue?fda9","webpack:///./src/components/backtester/result/chartWrapper.vue?d2a0","webpack:///./src/components/global/configbuilder/rangecreator.vue?c7ad","webpack:///./src/components/layout/modal.vue?a2b4","webpack:///./src/components/backtester/result/chartWrapper.vue?cfc8","webpack:///./src/d3/chart4.js","webpack:///./src/d3/message.js","webpack:///src/components/backtester/result/chartWrapper.vue","webpack:///./src/components/backtester/result/chartWrapper.vue?414d","webpack:///./src/components/backtester/result/chartWrapper.vue","webpack:///./src/main.js","webpack:///./src/components/backtester/result/summary.vue?6f4b","webpack:///./src/components/config/config.vue?9014","webpack:///./src/components/config/apiConfigBuilder.vue?901e","webpack:///./src/components/global/configbuilder/exchangepicker.vue?f5ea","webpack:///src/components/global/configbuilder/exchangepicker.vue","webpack:///./src/components/global/configbuilder/exchangepicker.vue?f140","webpack:///./src/components/global/configbuilder/exchangepicker.vue","webpack:///src/components/config/apiConfigBuilder.vue","webpack:///./src/components/config/apiConfigBuilder.vue?a340","webpack:///./src/components/config/apiConfigBuilder.vue","webpack:///src/components/config/config.vue","webpack:///./src/components/config/config.vue?03e8","webpack:///./src/components/config/config.vue","webpack:///./src/components/gekko/gekkoConfigBuilder.vue?c09b","webpack:///./src/components/global/configbuilder/rangepicker.vue?e445","webpack:///src/components/global/configbuilder/rangepicker.vue","webpack:///./src/components/global/configbuilder/rangepicker.vue?6482","webpack:///./src/components/global/configbuilder/rangepicker.vue","webpack:///./src/App.vue?8b0d","webpack:///./src/components/config/apiConfigBuilder.vue?bb40","webpack:///./src/components/backtester/backtester.vue?06dc","webpack:///./src/components/backtester/backtestConfigBuilder.vue?bafa","webpack:///./src/components/global/configbuilder/datasetpicker.vue?dc86","webpack:///src/components/global/configbuilder/datasetpicker.vue","webpack:///./src/components/global/configbuilder/datasetpicker.vue?adbd","webpack:///./src/components/global/configbuilder/datasetpicker.vue","webpack:///src/components/backtester/backtestConfigBuilder.vue","webpack:///./src/components/backtester/backtestConfigBuilder.vue?6bee","webpack:///./src/components/backtester/backtestConfigBuilder.vue","webpack:///./src/components/backtester/result/result.vue?f19d","webpack:///./src/components/backtester/result/summary.vue?a48b","webpack:///src/components/backtester/result/summary.vue","webpack:///./src/components/backtester/result/summary.vue?2c9f","webpack:///./src/components/backtester/result/summary.vue","webpack:///src/components/backtester/result/result.vue","webpack:///./src/components/backtester/result/result.vue?a333","webpack:///./src/components/backtester/result/result.vue","webpack:///src/components/backtester/backtester.vue","webpack:///./src/components/backtester/backtester.vue?b754","webpack:///./src/components/backtester/backtester.vue","webpack:///./src/components/data/data.vue?5f3a","webpack:///src/components/data/data.vue","webpack:///./src/components/data/data.vue?7897","webpack:///./src/components/data/data.vue","webpack:///./src/components/backtester/result/roundtripTable.vue?cb8d","webpack:///./src/components/gekko/list.vue?dec3","webpack:///./src/components/data/import/single.vue?892b","webpack:///./src/components/global/progressBar.vue?f48f","webpack:///src/components/global/progressBar.vue","webpack:///./src/components/global/progressBar.vue?3819","webpack:///./src/components/global/progressBar.vue","webpack:///src/components/data/import/single.vue","webpack:///./src/components/data/import/single.vue?ea36","webpack:///./src/components/data/import/single.vue","webpack:///./src/store/modules/imports/sync.js","webpack:///./src/store/modules/gekkos/sync.js","webpack:///./src/store/modules/notifications/sync.js","webpack:///./src/store/modules/config/sync.js","webpack:///./src/store/init.js","webpack:///./src/components/global/ws.js","webpack:///./src/components/gekko/list.vue?889e","webpack:///src/components/gekko/list.vue","webpack:///./src/components/gekko/list.vue?1de5","webpack:///./src/components/gekko/list.vue","webpack:///./src/components/data/import/importer.vue?370b","webpack:///./src/components/data/import/importConfigBuilder.vue?d697","webpack:///src/components/data/import/importConfigBuilder.vue","webpack:///./src/components/data/import/importConfigBuilder.vue?4a12","webpack:///./src/components/data/import/importConfigBuilder.vue","webpack:///src/components/data/import/importer.vue","webpack:///./src/components/data/import/importer.vue?5b1c","webpack:///./src/components/data/import/importer.vue","webpack:///./src/components/layout/home.vue?d339","webpack:///src/components/layout/home.vue","webpack:///./src/components/layout/home.vue?334a","webpack:///./src/components/layout/home.vue","webpack:///./src/components/gekko/singleGekko.vue?a7f7","webpack:///src/components/gekko/singleGekko.vue","webpack:///./src/components/gekko/singleGekko.vue?71ef","webpack:///./src/components/gekko/singleGekko.vue","webpack:///./src/components/global/configbuilder/datasetpicker.vue?0d02","webpack:///./src/components/global/blockSpinner.vue?69ce","webpack:///./src/components/data/import/single.vue?dfe8","webpack:///./src/tools/api.js","webpack:///./src/components/global/configbuilder/papertrader.vue?4204","webpack:///src/components/global/configbuilder/papertrader.vue","webpack:///./src/components/global/configbuilder/papertrader.vue?fdb2","webpack:///./src/components/global/configbuilder/papertrader.vue","webpack:///./src/components/global/mixins/dataset.js","webpack:///./src/components/global/configbuilder/stratpicker.vue?808f","webpack:///./src/components/global/paperTradeSummary.vue?c6c2","webpack:///./src/components/layout/header.vue?b5b8","webpack:///./src/components/backtester/result/roundtripTable.vue?64de","webpack:///src/components/backtester/result/roundtripTable.vue","webpack:///./src/components/backtester/result/roundtripTable.vue?35c4","webpack:///./src/components/backtester/result/roundtripTable.vue","webpack:///./src/components/backtester/result/result.vue?65dd","webpack:///./src/components/global/configbuilder/typepicker.vue?09f6","webpack:///./src/tools/ajax.js","webpack:///./src/components/data/import/importer.vue?3808"],"names":["webpackJsonpCallback","data","moduleId","chunkId","chunkIds","moreModules","executeModules","i","resolves","length","installedChunks","push","Object","prototype","hasOwnProperty","call","modules","parentJsonpFunction","shift","deferredModules","apply","checkDeferredModules","result","deferredModule","fulfilled","j","depId","splice","__webpack_require__","s","installedModules","1","exports","module","l","m","c","d","name","getter","o","defineProperty","enumerable","get","r","Symbol","toStringTag","value","t","mode","__esModule","ns","create","key","bind","n","object","property","p","jsonpArray","window","oldJsonpFunction","slice","_node_modules_mini_css_extract_plugin_dist_loader_js_node_modules_css_loader_index_js_ref_6_oneOf_1_1_node_modules_vue_loader_lib_loaders_stylePostLoader_js_node_modules_postcss_loader_lib_index_js_ref_6_oneOf_1_2_node_modules_cache_loader_dist_cjs_js_ref_0_0_node_modules_vue_loader_lib_index_js_vue_loader_options_singleGekko_vue_vue_type_style_index_0_lang_css__WEBPACK_IMPORTED_MODULE_0___default","_node_modules_mini_css_extract_plugin_dist_loader_js_node_modules_css_loader_index_js_ref_6_oneOf_1_1_node_modules_vue_loader_lib_loaders_stylePostLoader_js_node_modules_postcss_loader_lib_index_js_ref_6_oneOf_1_2_node_modules_cache_loader_dist_cjs_js_ref_0_0_node_modules_vue_loader_lib_index_js_vue_loader_options_progressBar_vue_vue_type_style_index_0_lang_css__WEBPACK_IMPORTED_MODULE_0___default","render","_vm","this","_h","$createElement","_c","_self","staticClass","_v","_s","report","trades","round2","sharpe","round","startBalance","currency","balance","_m","class","profitClass","relativeProfit","staticRenderFns","paperTradeSummaryvue_type_script_lang_js","props","methods","toFixed","computed","global_paperTradeSummaryvue_type_script_lang_js","component","componentNormalizer","__webpack_exports__","_node_modules_mini_css_extract_plugin_dist_loader_js_node_modules_css_loader_index_js_ref_6_oneOf_1_1_node_modules_vue_loader_lib_loaders_stylePostLoader_js_node_modules_postcss_loader_lib_index_js_ref_6_oneOf_1_2_node_modules_cache_loader_dist_cjs_js_ref_0_0_node_modules_vue_loader_lib_index_js_vue_loader_options_papertrader_vue_vue_type_style_index_0_lang_css__WEBPACK_IMPORTED_MODULE_0___default","_node_modules_mini_css_extract_plugin_dist_loader_js_node_modules_css_loader_index_js_ref_6_oneOf_1_1_node_modules_vue_loader_lib_loaders_stylePostLoader_js_node_modules_postcss_loader_lib_index_js_ref_6_oneOf_1_2_node_modules_cache_loader_dist_cjs_js_ref_0_0_node_modules_vue_loader_lib_index_js_vue_loader_options_new_vue_vue_type_style_index_0_lang_css__WEBPACK_IMPORTED_MODULE_0___default","on","config","updateConfig","pendingStratrunner","_e","attrs","href","click","$event","preventDefault","start","gekkoConfigBuildervue_type_template_id_6015a1e6_lang_pug_render","only-tradable","isTradebot","market","updateMarketConfig","type","updateType","stratConfig","updateStrat","settings","updatePaperTrader","gekkoConfigBuildervue_type_template_id_6015a1e6_lang_pug_staticRenderFns","typepickervue_type_template_id_4a1aa84c_lang_pug_render","for","_l","directives","rawName","expression","domProps","checked","_q","selectedTypeIndex","change","typepickervue_type_template_id_4a1aa84c_lang_pug_staticRenderFns","typepickervue_type_script_lang_js","created","emitType","types","$emit","watch","configbuilder_typepickervue_type_script_lang_js","typepicker","gekkoConfigBuildervue_type_script_lang_js","_this","ajax","error","response","candleWriter","toml","parse","part","performanceAnalyzer","enabled","range","strat","paperTrader","components","marketPicker","marketpicker","typePicker","stratPicker","stratpicker","papertrader","assign","trader","valid","validConfig","tradingAdvisor","lodash_default","a","isNaN","candleSize","method","isEmpty","mc","emitConfig","pt","gekko_gekkoConfigBuildervue_type_script_lang_js","gekkoConfigBuilder_component","gekkoConfigBuilder","newvue_type_script_lang_js","spinner","blockSpinner","gekkos","$store","state","watchConfig","raw","pick","vue_runtime_esm","util","extend","requiredHistoricalData","stratSettings","historySize","gekkoConfig","startAt","existingMarketWatcher","optimal","moment","utc","startOf","subtract","unix","available","events","initial","candle","Math","max","format","from","find","exchange","existingTradebot","g","logType","availableApiKeys","apiKeys","val","prev","_this2","gekko","latest","startGekko","err","resp","$router","path","concat","id","_this3","str","alert","includes","startWatcher","routeToGekko","console","next","gekko_newvue_type_script_lang_js","new_component","_node_modules_mini_css_extract_plugin_dist_loader_js_node_modules_css_loader_index_js_ref_6_oneOf_1_1_node_modules_vue_loader_lib_loaders_stylePostLoader_js_node_modules_postcss_loader_lib_index_js_ref_6_oneOf_1_2_node_modules_cache_loader_dist_cjs_js_ref_0_0_node_modules_vue_loader_lib_index_js_vue_loader_options_data_vue_vue_type_style_index_0_lang_css__WEBPACK_IMPORTED_MODULE_0___default","marked","marked__WEBPACK_IMPORTED_MODULE_0___default","myRenderer","Renderer","link","title","text","external","newWindow","out","test","setOptions","renderer","$$selectedVal","Array","filter","target","options","selected","map","_value","multiple","e","cur","asset","asst","marketpickervue_type_script_lang_js","exchanges","onlyTradable","each","tradable","onlyImportable","importable","markets","assets","currencies","keys","configbuilder_marketpickervue_type_script_lang_js","strategy","input","composing","rawCandleSize","candleSizeUnit","singularCandleSizeUnit","humanizeDuration","rawStratParams","rawStratParamsError","message","stratpickervue_type_script_lang_js","strategies","emptyStrat","stratParams","empty","params","__empty","parseParams","configbuilder_stratpickervue_type_script_lang_js","_node_modules_mini_css_extract_plugin_dist_loader_js_node_modules_css_loader_index_js_ref_6_oneOf_1_1_node_modules_vue_loader_lib_loaders_stylePostLoader_js_node_modules_postcss_loader_lib_index_js_ref_6_oneOf_1_2_node_modules_cache_loader_dist_cjs_js_ref_0_0_node_modules_vue_loader_lib_index_js_vue_loader_options_rangepicker_vue_vue_type_style_index_0_lang_css__WEBPACK_IMPORTED_MODULE_0___default","to","rangecreatorvue_type_script_lang_js","now","then","clone","fmt","emitRange","fmtTs","mom","emitManualEntry","isValid","scanned","tab","selectedRangeIndex","selectedRange","ranges","configbuilder_rangecreatorvue_type_script_lang_js","_node_modules_mini_css_extract_plugin_dist_loader_js_node_modules_css_loader_index_js_ref_6_oneOf_1_1_node_modules_vue_loader_lib_loaders_stylePostLoader_js_node_modules_postcss_loader_lib_index_js_ref_6_oneOf_1_2_node_modules_cache_loader_dist_cjs_js_ref_0_0_node_modules_vue_loader_lib_index_js_vue_loader_options_importConfigBuilder_vue_vue_type_style_index_0_lang_css__WEBPACK_IMPORTED_MODULE_0___default","blockSpinnervue_type_script_lang_js","global_blockSpinnervue_type_script_lang_js","headervue_type_template_id_0a62d360_lang_pug_render","headervue_type_template_id_0a62d360_lang_pug_staticRenderFns","headervue_type_script_lang_js","layout_headervue_type_script_lang_js","header","footervue_type_template_id_7782dc11_lang_pug_render","version","ui","footervue_type_template_id_7782dc11_lang_pug_staticRenderFns","gekkoPackage","gekko_package","uiPackage","package_0","footervue_type_script_lang_js","layout_footervue_type_script_lang_js","footer_component","footer","modalvue_type_template_id_340a7d17_lang_pug_render","innerHTML","content","modalvue_type_template_id_340a7d17_lang_pug_staticRenderFns","messages","disconnected","modalvue_type_script_lang_js","active","warnings","connected","layout_modalvue_type_script_lang_js","modal_component","modal","Appvue_type_script_lang_js","top","bottom","src_Appvue_type_script_lang_js","App_component","addImport","imp","imports","syncImports","mutations_updateImport","update","index","findIndex","import_id","item","updated","updates","set","skipInitialEvents","skipLatestEvents","trackAllEvents","reduceState_reduce","event","payload","objectSpread","latestUpdate","Date","toConsumableArray","mutations_reduceState","reduceState","syncGekkos","live","archivedGekkos","archive","mutations_addGekko","mutations_updateGekko","has","mutations_archiveGekko","stopped","omit","mutations_errorGekko","errored","errorMessage","mutations_deleteGekko","setGlobalWarning","warning","mutations_syncApiKeys","mutations_syncExchanges","use","vuex_esm","debug","mutations","merge","mutations_namespaceObject","gekkos_mutations_namespaceObject","notifications_mutations_namespaceObject","config_mutations_namespaceObject","Store","connection","reconnected","strict","_node_modules_mini_css_extract_plugin_dist_loader_js_node_modules_css_loader_index_js_ref_6_oneOf_1_1_node_modules_vue_loader_lib_loaders_stylePostLoader_js_node_modules_postcss_loader_lib_index_js_ref_6_oneOf_1_2_node_modules_cache_loader_dist_cjs_js_ref_0_0_node_modules_vue_loader_lib_index_js_vue_loader_options_backtestConfigBuilder_vue_vue_type_style_index_0_lang_css__WEBPACK_IMPORTED_MODULE_0___default","_node_modules_mini_css_extract_plugin_dist_loader_js_node_modules_css_loader_index_js_ref_6_oneOf_1_1_node_modules_vue_loader_lib_loaders_stylePostLoader_js_node_modules_postcss_loader_lib_index_js_ref_6_oneOf_1_2_node_modules_cache_loader_dist_cjs_js_ref_0_0_node_modules_vue_loader_lib_index_js_vue_loader_options_chartWrapper_vue_vue_type_style_index_0_lang_css__WEBPACK_IMPORTED_MODULE_0___default","_node_modules_mini_css_extract_plugin_dist_loader_js_node_modules_css_loader_index_js_ref_6_oneOf_1_1_node_modules_vue_loader_lib_loaders_stylePostLoader_js_node_modules_postcss_loader_lib_index_js_ref_6_oneOf_1_2_node_modules_cache_loader_dist_cjs_js_ref_0_0_node_modules_vue_loader_lib_index_js_vue_loader_options_rangecreator_vue_vue_type_style_index_0_lang_css__WEBPACK_IMPORTED_MODULE_0___default","_node_modules_mini_css_extract_plugin_dist_loader_js_node_modules_css_loader_index_js_ref_6_oneOf_1_1_node_modules_vue_loader_lib_loaders_stylePostLoader_js_node_modules_postcss_loader_lib_index_js_ref_6_oneOf_1_2_node_modules_cache_loader_dist_cjs_js_ref_0_0_node_modules_vue_loader_lib_index_js_vue_loader_options_modal_vue_vue_type_style_index_0_lang_css__WEBPACK_IMPORTED_MODULE_0___default","chartWrappervue_type_template_id_12bcb8d8_lang_pug_render","clickable","isClicked","width","height","_data","_trades","_height","toDate","isNumber","price","date","action","open","dates","prices","svg","d3","select","attr","innerWidth","margin","right","left","margin2","height2","x","scaleUtc","x2","y","scaleLinear","y2","xAxis","axisBottom","xAxis2","yAxis","axisLeft","ticks","brush","brushX","extent","brushed","zoom","scaleExtent","translateExtent","zoomed","line","line2","append","focus","context","domain","min","datum","circles","selectAll","enter","sourceEvent","selection","invert","scaleY","transform","zoomIdentity","scale","translate","_domain","slicedToArray","minIndex","sortedIndex","maxIndex","rescaleX","move","invertX","draw","MIN_CANDLES","chartWrappervue_type_script_lang_js","setTimeout","beforeDestroy","remove","_","size","candles","chart4","html","result_chartWrappervue_type_script_lang_js","vue__WEBPACK_IMPORTED_MODULE_1__","vue_router__WEBPACK_IMPORTED_MODULE_3__","router","base","__dirname","routes","redirect","_components_layout_home_vue__WEBPACK_IMPORTED_MODULE_6__","_components_backtester_backtester_vue__WEBPACK_IMPORTED_MODULE_5__","_components_config_config_vue__WEBPACK_IMPORTED_MODULE_10__","_components_data_data_vue__WEBPACK_IMPORTED_MODULE_7__","_components_data_import_importer_vue__WEBPACK_IMPORTED_MODULE_8__","_components_data_import_single_vue__WEBPACK_IMPORTED_MODULE_9__","_components_gekko_list_vue__WEBPACK_IMPORTED_MODULE_11__","_components_gekko_new_vue__WEBPACK_IMPORTED_MODULE_12__","_components_gekko_singleGekko_vue__WEBPACK_IMPORTED_MODULE_13__","_components_global_ws__WEBPACK_IMPORTED_MODULE_14__","store","_store__WEBPACK_IMPORTED_MODULE_4__","el","h","_App_vue__WEBPACK_IMPORTED_MODULE_2__","_node_modules_mini_css_extract_plugin_dist_loader_js_node_modules_css_loader_index_js_ref_6_oneOf_1_1_node_modules_vue_loader_lib_loaders_stylePostLoader_js_node_modules_postcss_loader_lib_index_js_ref_6_oneOf_1_2_node_modules_cache_loader_dist_cjs_js_ref_0_0_node_modules_vue_loader_lib_index_js_vue_loader_options_summary_vue_vue_type_style_index_0_lang_css__WEBPACK_IMPORTED_MODULE_0___default","apiKeySets","removeApiKey","addApiToggle","openAddApi","apiConfigBuildervue_type_template_id_4a31b09e_lang_pug_render","updateExchange","cred","credentials","$set","upload","apiConfigBuildervue_type_template_id_4a31b09e_lang_pug_staticRenderFns","exchangepickervue_type_template_id_50c29cb0_lang_pug_render","exchangepickervue_type_template_id_50c29cb0_lang_pug_staticRenderFns","exchangepickervue_type_script_lang_js","emitExchange","configbuilder_exchangepickervue_type_script_lang_js","exchangepicker","apiConfigBuildervue_type_script_lang_js","exchangePicker","requires","values","confirm","config_apiConfigBuildervue_type_script_lang_js","apiConfigBuilder_component","apiConfigBuilder","configvue_type_script_lang_js","config_configvue_type_script_lang_js","config_component","_node_modules_mini_css_extract_plugin_dist_loader_js_node_modules_css_loader_index_js_ref_6_oneOf_1_1_node_modules_vue_loader_lib_loaders_stylePostLoader_js_node_modules_postcss_loader_lib_index_js_ref_6_oneOf_1_2_node_modules_cache_loader_dist_cjs_js_ref_0_0_node_modules_vue_loader_lib_index_js_vue_loader_options_gekkoConfigBuilder_vue_vue_type_style_index_0_lang_css__WEBPACK_IMPORTED_MODULE_0___default","scan","printRange","rangepickervue_type_script_lang_js","diff","duration","humanize","reset","configbuilder_rangepickervue_type_script_lang_js","_node_modules_mini_css_extract_plugin_dist_loader_js_node_modules_css_loader_index_js_ref_6_oneOf_1_1_node_modules_vue_loader_lib_loaders_stylePostLoader_js_node_modules_postcss_loader_lib_index_js_ref_6_oneOf_1_2_node_modules_cache_loader_dist_cjs_js_ref_0_0_node_modules_vue_loader_lib_index_js_vue_loader_options_App_vue_vue_type_style_index_0_lang_css__WEBPACK_IMPORTED_MODULE_0___default","_node_modules_mini_css_extract_plugin_dist_loader_js_node_modules_css_loader_index_js_ref_6_oneOf_1_1_node_modules_vue_loader_lib_loaders_stylePostLoader_js_node_modules_postcss_loader_lib_index_js_ref_6_oneOf_1_2_node_modules_cache_loader_dist_cjs_js_ref_0_0_node_modules_vue_loader_lib_index_js_vue_loader_options_apiConfigBuilder_vue_vue_type_style_index_0_lang_css__WEBPACK_IMPORTED_MODULE_0___default","check","backtestState","run","backtestResult","backtestConfigBuildervue_type_template_id_0b74d9a3_lang_pug_render","dataset","updateDataset","backtestConfigBuildervue_type_template_id_0b74d9a3_lang_pug_staticRenderFns","datasetpickervue_type_template_id_689b8a00_lang_pug_render","datasetScanstate","datasets","setIndex","rangeVisible","openRange","customFrom","customTo","datasetpickervue_type_template_id_689b8a00_lang_pug_staticRenderFns","datasetpickervue_type_script_lang_js","mixins","largest","updateCustomRange","emitSet","configbuilder_datasetpickervue_type_script_lang_js","datasetpicker","backtestConfigBuildervue_type_script_lang_js","datasetPicker","backtest","daterange","backtestResultExporter","writeToDisk","stratUpdates","roundtrips","stratCandles","stratCandleProps","sc","backtester_backtestConfigBuildervue_type_script_lang_js","backtestConfigBuilder_component","backtestConfigBuilder","resultvue_type_template_id_72b63acc_lang_pug_render","performanceReport","resultvue_type_template_id_72b63acc_lang_pug_staticRenderFns","summaryvue_type_template_id_5bfc891d_lang_pug_render","startTime","endTime","timespan","startPrice","endPrice","summaryvue_type_template_id_5bfc891d_lang_pug_staticRenderFns","summaryvue_type_script_lang_js","paperTradeSummary","result_summaryvue_type_script_lang_js","summary_component","summary","resultvue_type_script_lang_js","roundtripTable","resultSummary","chart","chartWrapper","result_resultvue_type_script_lang_js","result_component","backtestervue_type_script_lang_js","backtestable","configBuilder","backtester_backtestervue_type_script_lang_js","backtester_component","intro","unscannableMakets","viewUnscannable","toggleUnscannable","datavue_type_script_lang_js","data_datavue_type_script_lang_js","_node_modules_mini_css_extract_plugin_dist_loader_js_node_modules_css_loader_index_js_ref_6_oneOf_1_1_node_modules_vue_loader_lib_loaders_stylePostLoader_js_node_modules_postcss_loader_lib_index_js_ref_6_oneOf_1_2_node_modules_cache_loader_dist_cjs_js_ref_0_0_node_modules_vue_loader_lib_index_js_vue_loader_options_roundtripTable_vue_vue_type_style_index_0_lang_css__WEBPACK_IMPORTED_MODULE_0___default","_node_modules_mini_css_extract_plugin_dist_loader_js_node_modules_css_loader_index_js_ref_6_oneOf_1_1_node_modules_vue_loader_lib_loaders_stylePostLoader_js_node_modules_postcss_loader_lib_index_js_ref_6_oneOf_1_2_node_modules_cache_loader_dist_cjs_js_ref_0_0_node_modules_vue_loader_lib_index_js_vue_loader_options_list_vue_vue_type_style_index_0_lang_css__WEBPACK_IMPORTED_MODULE_0___default","done","fromEnd","initialized","progress","progressBarvue_type_template_id_2b42ff94_lang_pug_render","style","progressBarvue_type_template_id_2b42ff94_lang_pug_staticRenderFns","progressBarvue_type_script_lang_js","global_progressBarvue_type_script_lang_js","progressBar","singlevue_type_script_lang_js","$route","fromEndMs","current","import_singlevue_type_script_lang_js","single_component","sync_init","commit","sync_sync","bus","$on","imports_sync","gekkos_sync_init","gekkos_sync_sync","gekkos_sync","notifications_sync_init","notifications_sync_sync","ws","notifications_sync","sync_transformMarkets","backendData","exchangesRaw","exchangesTemp","forEach","slug","pair","_pair$pair","exchangeMaxHistoryAge","providesFullHistory","config_sync_init","config_sync_sync","config_sync","store_init","socket","log","info","ws_connect","ReconnectingWebSocket","api","maxReconnectInterval","onopen","onclose","onerror","onmessage","JSON","watchers","status","stratrunners","profit","tradeCompleted","listvue_type_script_lang_js","timer","setInterval","destroyed","clearTimeout","_moment","_x","arguments","toString","b","gekko_listvue_type_script_lang_js","_import","importConfigBuildervue_type_template_id_a8c578c6_lang_pug_render","only-importable","updateRange","importConfigBuildervue_type_template_id_a8c578c6_lang_pug_staticRenderFns","importConfigBuildervue_type_script_lang_js","rangeCreator","rangecreator","importer","import_importConfigBuildervue_type_script_lang_js","importConfigBuilder","importervue_type_script_lang_js","daysApart","import_importervue_type_script_lang_js","importer_component","src","homevue_type_script_lang_js","layout_homevue_type_script_lang_js","isLoading","initialEvents","latestEvents","warmupRemaining","replace","stratName","isArchived","alpha","isStratrunner","watcher","stopGekko","deleteGekko","candleFetch","chartData","singleGekkovue_type_script_lang_js","getCandles","isLive","stratWarmupCompleted","warmupTime","add","stringify","isEqual","hasLeechers","data.events.latest.candle.start","random","res","isArray","_this4","gekko_singleGekkovue_type_script_lang_js","_node_modules_mini_css_extract_plugin_dist_loader_js_node_modules_css_loader_index_js_ref_6_oneOf_1_1_node_modules_vue_loader_lib_loaders_stylePostLoader_js_node_modules_postcss_loader_lib_index_js_ref_6_oneOf_1_2_node_modules_cache_loader_dist_cjs_js_ref_0_0_node_modules_vue_loader_lib_index_js_vue_loader_options_datasetpicker_vue_vue_type_style_index_0_lang_css__WEBPACK_IMPORTED_MODULE_0___default","_node_modules_mini_css_extract_plugin_dist_loader_js_node_modules_css_loader_index_js_ref_6_oneOf_1_1_node_modules_vue_loader_lib_loaders_stylePostLoader_js_node_modules_postcss_loader_lib_index_js_ref_6_oneOf_1_2_node_modules_cache_loader_dist_cjs_js_ref_0_0_node_modules_vue_loader_lib_index_js_vue_loader_options_blockSpinner_vue_vue_type_style_index_0_lang_css__WEBPACK_IMPORTED_MODULE_0___default","_node_modules_mini_css_extract_plugin_dist_loader_js_node_modules_css_loader_index_js_ref_6_oneOf_1_1_node_modules_vue_loader_lib_loaders_stylePostLoader_js_node_modules_postcss_loader_lib_index_js_ref_6_oneOf_1_2_node_modules_cache_loader_dist_cjs_js_ref_0_0_node_modules_vue_loader_lib_index_js_vue_loader_options_single_vue_vue_type_style_index_0_lang_css__WEBPACK_IMPORTED_MODULE_0___default","basePath","restPath","wsPath","CONFIG","endpoint","host","port","ssl","toggle","switchToggle","rawPaperTraderParams","rawPaperTraderParamsError","papertradervue_type_script_lang_js","paperTraderParams","reportRoundtrips","configbuilder_papertradervue_type_script_lang_js","mixin","_tools_ajax__WEBPACK_IMPORTED_MODULE_2__","errors","sets","sort","adiff","bdiff","reverse","_node_modules_mini_css_extract_plugin_dist_loader_js_node_modules_css_loader_index_js_ref_6_oneOf_1_1_node_modules_vue_loader_lib_loaders_stylePostLoader_js_node_modules_postcss_loader_lib_index_js_ref_6_oneOf_1_2_node_modules_cache_loader_dist_cjs_js_ref_0_0_node_modules_vue_loader_lib_index_js_vue_loader_options_stratpicker_vue_vue_type_style_index_0_lang_css__WEBPACK_IMPORTED_MODULE_0___default","_node_modules_mini_css_extract_plugin_dist_loader_js_node_modules_css_loader_index_js_ref_6_oneOf_1_1_node_modules_vue_loader_lib_loaders_stylePostLoader_js_node_modules_postcss_loader_lib_index_js_ref_6_oneOf_1_2_node_modules_cache_loader_dist_cjs_js_ref_0_0_node_modules_vue_loader_lib_index_js_vue_loader_options_paperTradeSummary_vue_vue_type_style_index_0_lang_css__WEBPACK_IMPORTED_MODULE_0___default","_node_modules_mini_css_extract_plugin_dist_loader_js_node_modules_css_loader_index_js_ref_6_oneOf_1_1_node_modules_vue_loader_lib_loaders_stylePostLoader_js_node_modules_postcss_loader_lib_index_js_ref_6_oneOf_1_2_node_modules_cache_loader_dist_cjs_js_ref_0_0_node_modules_vue_loader_lib_index_js_vue_loader_options_header_vue_vue_type_style_index_0_lang_css__WEBPACK_IMPORTED_MODULE_0___default","rt","entryAt","exitAt","entryBalance","exitBalance","sign","pnl","roundtripTablevue_type_script_lang_js","result_roundtripTablevue_type_script_lang_js","_node_modules_mini_css_extract_plugin_dist_loader_js_node_modules_css_loader_index_js_ref_6_oneOf_1_1_node_modules_vue_loader_lib_loaders_stylePostLoader_js_node_modules_postcss_loader_lib_index_js_ref_6_oneOf_1_2_node_modules_cache_loader_dist_cjs_js_ref_0_0_node_modules_vue_loader_lib_index_js_vue_loader_options_result_vue_vue_type_style_index_0_lang_css__WEBPACK_IMPORTED_MODULE_0___default","_node_modules_mini_css_extract_plugin_dist_loader_js_node_modules_css_loader_index_js_ref_6_oneOf_1_1_node_modules_vue_loader_lib_loaders_stylePostLoader_js_node_modules_postcss_loader_lib_index_js_ref_6_oneOf_1_2_node_modules_cache_loader_dist_cjs_js_ref_0_0_node_modules_vue_loader_lib_index_js_vue_loader_options_typepicker_vue_vue_type_style_index_0_lang_css__WEBPACK_IMPORTED_MODULE_0___default","processResponse","post","superagent__WEBPACK_IMPORTED_MODULE_0___default","_api_js__WEBPACK_IMPORTED_MODULE_2__","superagent_no_cache__WEBPACK_IMPORTED_MODULE_1___default","send","end","_node_modules_mini_css_extract_plugin_dist_loader_js_node_modules_css_loader_index_js_ref_6_oneOf_1_1_node_modules_vue_loader_lib_loaders_stylePostLoader_js_node_modules_postcss_loader_lib_index_js_ref_6_oneOf_1_2_node_modules_cache_loader_dist_cjs_js_ref_0_0_node_modules_vue_loader_lib_index_js_vue_loader_options_importer_vue_vue_type_style_index_0_lang_css__WEBPACK_IMPORTED_MODULE_0___default"],"mappings":"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","file":"app.5e99ecf7.js","sourcesContent":[" \t// install a JSONP callback for chunk loading\n \tfunction webpackJsonpCallback(data) {\n \t\tvar chunkIds = data[0];\n \t\tvar moreModules = data[1];\n \t\tvar executeModules = data[2];\n\n \t\t// add \"moreModules\" to the modules object,\n \t\t// then flag all \"chunkIds\" as loaded and fire callback\n \t\tvar moduleId, chunkId, i = 0, resolves = [];\n \t\tfor(;i < chunkIds.length; i++) {\n \t\t\tchunkId = chunkIds[i];\n \t\t\tif(installedChunks[chunkId]) {\n \t\t\t\tresolves.push(installedChunks[chunkId][0]);\n \t\t\t}\n \t\t\tinstalledChunks[chunkId] = 0;\n \t\t}\n \t\tfor(moduleId in moreModules) {\n \t\t\tif(Object.prototype.hasOwnProperty.call(moreModules, moduleId)) {\n \t\t\t\tmodules[moduleId] = moreModules[moduleId];\n \t\t\t}\n \t\t}\n \t\tif(parentJsonpFunction) parentJsonpFunction(data);\n\n \t\twhile(resolves.length) {\n \t\t\tresolves.shift()();\n \t\t}\n\n \t\t// add entry modules from loaded chunk to deferred list\n \t\tdeferredModules.push.apply(deferredModules, executeModules || []);\n\n \t\t// run deferred modules when all chunks ready\n \t\treturn checkDeferredModules();\n \t};\n \tfunction checkDeferredModules() {\n \t\tvar result;\n \t\tfor(var i = 0; i < deferredModules.length; i++) {\n \t\t\tvar deferredModule = deferredModules[i];\n \t\t\tvar fulfilled = true;\n \t\t\tfor(var j = 1; j < deferredModule.length; j++) {\n \t\t\t\tvar depId = deferredModule[j];\n \t\t\t\tif(installedChunks[depId] !== 0) fulfilled = false;\n \t\t\t}\n \t\t\tif(fulfilled) {\n \t\t\t\tdeferredModules.splice(i--, 1);\n \t\t\t\tresult = __webpack_require__(__webpack_require__.s = deferredModule[0]);\n \t\t\t}\n \t\t}\n \t\treturn result;\n \t}\n\n \t// The module cache\n \tvar installedModules = {};\n\n \t// object to store loaded and loading chunks\n \t// undefined = chunk not loaded, null = chunk preloaded/prefetched\n \t// Promise = chunk loading, 0 = chunk loaded\n \tvar installedChunks = {\n \t\t1: 0\n \t};\n\n \tvar deferredModules = [];\n\n \t// The require function\n \tfunction __webpack_require__(moduleId) {\n\n \t\t// Check if module is in cache\n \t\tif(installedModules[moduleId]) {\n \t\t\treturn installedModules[moduleId].exports;\n \t\t}\n \t\t// Create a new module (and put it into the cache)\n \t\tvar module = installedModules[moduleId] = {\n \t\t\ti: moduleId,\n \t\t\tl: false,\n \t\t\texports: {}\n \t\t};\n\n \t\t// Execute the module function\n \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n \t\t// Flag the module as loaded\n \t\tmodule.l = true;\n\n \t\t// Return the exports of the module\n \t\treturn module.exports;\n \t}\n\n\n \t// expose the modules object (__webpack_modules__)\n \t__webpack_require__.m = modules;\n\n \t// expose the module cache\n \t__webpack_require__.c = installedModules;\n\n \t// define getter function for harmony exports\n \t__webpack_require__.d = function(exports, name, getter) {\n \t\tif(!__webpack_require__.o(exports, name)) {\n \t\t\tObject.defineProperty(exports, name, { enumerable: true, get: getter });\n \t\t}\n \t};\n\n \t// define __esModule on exports\n \t__webpack_require__.r = function(exports) {\n \t\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n \t\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n \t\t}\n \t\tObject.defineProperty(exports, '__esModule', { value: true });\n \t};\n\n \t// create a fake namespace object\n \t// mode & 1: value is a module id, require it\n \t// mode & 2: merge all properties of value into the ns\n \t// mode & 4: return value when already ns object\n \t// mode & 8|1: behave like require\n \t__webpack_require__.t = function(value, mode) {\n \t\tif(mode & 1) value = __webpack_require__(value);\n \t\tif(mode & 8) return value;\n \t\tif((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;\n \t\tvar ns = Object.create(null);\n \t\t__webpack_require__.r(ns);\n \t\tObject.defineProperty(ns, 'default', { enumerable: true, value: value });\n \t\tif(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));\n \t\treturn ns;\n \t};\n\n \t// getDefaultExport function for compatibility with non-harmony modules\n \t__webpack_require__.n = function(module) {\n \t\tvar getter = module && module.__esModule ?\n \t\t\tfunction getDefault() { return module['default']; } :\n \t\t\tfunction getModuleExports() { return module; };\n \t\t__webpack_require__.d(getter, 'a', getter);\n \t\treturn getter;\n \t};\n\n \t// Object.prototype.hasOwnProperty.call\n \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n\n \t// __webpack_public_path__\n \t__webpack_require__.p = \"\";\n\n \tvar jsonpArray = window[\"webpackJsonp\"] = window[\"webpackJsonp\"] || [];\n \tvar oldJsonpFunction = jsonpArray.push.bind(jsonpArray);\n \tjsonpArray.push = webpackJsonpCallback;\n \tjsonpArray = jsonpArray.slice();\n \tfor(var i = 0; i < jsonpArray.length; i++) webpackJsonpCallback(jsonpArray[i]);\n \tvar parentJsonpFunction = oldJsonpFunction;\n\n\n \t// add entry module to deferred list\n \tdeferredModules.push([26,0]);\n \t// run deferred modules when ready\n \treturn checkDeferredModules();\n","import mod from \"-!../../../node_modules/mini-css-extract-plugin/dist/loader.js!../../../node_modules/css-loader/index.js??ref--6-oneOf-1-1!../../../node_modules/vue-loader/lib/loaders/stylePostLoader.js!../../../node_modules/postcss-loader/lib/index.js??ref--6-oneOf-1-2!../../../node_modules/cache-loader/dist/cjs.js??ref--0-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./singleGekko.vue?vue&type=style&index=0&lang=css\"; export default mod; export * from \"-!../../../node_modules/mini-css-extract-plugin/dist/loader.js!../../../node_modules/css-loader/index.js??ref--6-oneOf-1-1!../../../node_modules/vue-loader/lib/loaders/stylePostLoader.js!../../../node_modules/postcss-loader/lib/index.js??ref--6-oneOf-1-2!../../../node_modules/cache-loader/dist/cjs.js??ref--0-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./singleGekko.vue?vue&type=style&index=0&lang=css\"","import mod from \"-!../../../node_modules/mini-css-extract-plugin/dist/loader.js!../../../node_modules/css-loader/index.js??ref--6-oneOf-1-1!../../../node_modules/vue-loader/lib/loaders/stylePostLoader.js!../../../node_modules/postcss-loader/lib/index.js??ref--6-oneOf-1-2!../../../node_modules/cache-loader/dist/cjs.js??ref--0-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./progressBar.vue?vue&type=style&index=0&lang=css\"; export default mod; export * from \"-!../../../node_modules/mini-css-extract-plugin/dist/loader.js!../../../node_modules/css-loader/index.js??ref--6-oneOf-1-1!../../../node_modules/vue-loader/lib/loaders/stylePostLoader.js!../../../node_modules/postcss-loader/lib/index.js??ref--6-oneOf-1-2!../../../node_modules/cache-loader/dist/cjs.js??ref--0-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./progressBar.vue?vue&type=style&index=0&lang=css\"","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"grd-row-col-3-6\"},[_c('table',{staticClass:\"p1\"},[_c('tr',[_c('th',[_vm._v(\"amount of trades\")]),_c('td',[_vm._v(_vm._s(_vm.report.trades))])]),_c('tr',[_c('th',[_vm._v(\"sharpe ratio\")]),_c('td',[_vm._v(_vm._s(_vm.round2(_vm.report.sharpe)))])]),_c('tr',[_c('th',[_vm._v(\"start balance\")]),_c('td',[_vm._v(_vm._s(_vm.round(_vm.report.startBalance))+\" \"+_vm._s(_vm.report.currency))])]),_c('tr',[_c('th',[_vm._v(\"final balance\")]),_c('td',[_vm._v(_vm._s(_vm.round(_vm.report.balance))+\" \"+_vm._s(_vm.report.currency))])]),_vm._m(0)]),_c('div',{staticClass:\"big txt--right price\",class:_vm.profitClass},[_vm._v(_vm._s(_vm.round(_vm.report.relativeProfit))+\"%\")])])}\nvar staticRenderFns = [function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('tr',[_c('th',[_vm._v(\"simulated profit\")])])}]\n\nexport { render, staticRenderFns }","\n\n\n\n\n","import mod from \"-!../../../node_modules/cache-loader/dist/cjs.js??ref--12-0!../../../node_modules/thread-loader/dist/cjs.js!../../../node_modules/babel-loader/lib/index.js!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./paperTradeSummary.vue?vue&type=script&lang=js\"; export default mod; export * from \"-!../../../node_modules/cache-loader/dist/cjs.js??ref--12-0!../../../node_modules/thread-loader/dist/cjs.js!../../../node_modules/babel-loader/lib/index.js!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./paperTradeSummary.vue?vue&type=script&lang=js\"","import { render, staticRenderFns } from \"./paperTradeSummary.vue?vue&type=template&id=637e135e&lang=pug\"\nimport script from \"./paperTradeSummary.vue?vue&type=script&lang=js\"\nexport * from \"./paperTradeSummary.vue?vue&type=script&lang=js\"\nimport style0 from \"./paperTradeSummary.vue?vue&type=style&index=0&lang=css\"\n\n\n/* normalize component */\nimport normalizer from \"!../../../node_modules/vue-loader/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n null,\n null\n \n)\n\nexport default component.exports","import mod from \"-!../../../../node_modules/mini-css-extract-plugin/dist/loader.js!../../../../node_modules/css-loader/index.js??ref--6-oneOf-1-1!../../../../node_modules/vue-loader/lib/loaders/stylePostLoader.js!../../../../node_modules/postcss-loader/lib/index.js??ref--6-oneOf-1-2!../../../../node_modules/cache-loader/dist/cjs.js??ref--0-0!../../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./papertrader.vue?vue&type=style&index=0&lang=css\"; export default mod; export * from \"-!../../../../node_modules/mini-css-extract-plugin/dist/loader.js!../../../../node_modules/css-loader/index.js??ref--6-oneOf-1-1!../../../../node_modules/vue-loader/lib/loaders/stylePostLoader.js!../../../../node_modules/postcss-loader/lib/index.js??ref--6-oneOf-1-2!../../../../node_modules/cache-loader/dist/cjs.js??ref--0-0!../../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./papertrader.vue?vue&type=style&index=0&lang=css\"","import mod from \"-!../../../node_modules/mini-css-extract-plugin/dist/loader.js!../../../node_modules/css-loader/index.js??ref--6-oneOf-1-1!../../../node_modules/vue-loader/lib/loaders/stylePostLoader.js!../../../node_modules/postcss-loader/lib/index.js??ref--6-oneOf-1-2!../../../node_modules/cache-loader/dist/cjs.js??ref--0-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./new.vue?vue&type=style&index=0&lang=css\"; export default mod; export * from \"-!../../../node_modules/mini-css-extract-plugin/dist/loader.js!../../../node_modules/css-loader/index.js??ref--6-oneOf-1-1!../../../node_modules/vue-loader/lib/loaders/stylePostLoader.js!../../../node_modules/postcss-loader/lib/index.js??ref--6-oneOf-1-2!../../../node_modules/cache-loader/dist/cjs.js??ref--0-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./new.vue?vue&type=style&index=0&lang=css\"","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"contain my2\"},[_c('h3',[_vm._v(\"Start a new gekko\")]),_c('gekko-config-builder',{on:{\"config\":_vm.updateConfig}}),_c('div',{staticClass:\"hr\"}),(_vm.config.valid)?_c('div',{staticClass:\"txt--center\"},[(!_vm.pendingStratrunner)?_c('a',{staticClass:\"w100--s my1 btn--primary\",attrs:{\"href\":\"#\"},on:{\"click\":function($event){$event.preventDefault();return _vm.start($event)}}},[_vm._v(\"Start\")]):_vm._e(),(_vm.pendingStratrunner)?_c('spinner'):_vm._e()],1):_vm._e()],1)}\nvar staticRenderFns = []\n\nexport { render, staticRenderFns }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"grd contain\"},[_c('div',{staticClass:\"grd-row\"},[_c('div',{staticClass:\"grd-row-col-3-6 mx1\"},[_c('h3',[_vm._v(\"Market\")]),_c('market-picker',{attrs:{\"only-tradable\":_vm.isTradebot},on:{\"market\":_vm.updateMarketConfig}})],1),_c('div',{staticClass:\"grd-row-col-3-6 mx1\"},[_c('type-picker',{on:{\"type\":_vm.updateType}})],1)]),(_vm.type !== \"market watcher\")?[_c('div',{staticClass:\"hr\"}),_c('strat-picker',{staticClass:\"contain my2\",on:{\"stratConfig\":_vm.updateStrat}}),(_vm.type === \"paper trader\")?_c('div',{staticClass:\"hr\"}):_vm._e(),(_vm.type === \"paper trader\")?_c('paper-trader',{on:{\"settings\":_vm.updatePaperTrader}}):_vm._e()]:_vm._e()],2)}\nvar staticRenderFns = []\n\nexport { render, staticRenderFns }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',[_c('h3',[_vm._v(\"Type\")]),[_c('label',{staticClass:\"wrapper\",attrs:{\"for\":\"type\"}},[_vm._v(\"What do you want to do with gekko?\")]),_c('form',{staticClass:\"radio grd\"},_vm._l((_vm.types),function(type,i){return _c('div',{staticClass:\"grd-row m1\"},[_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.selectedTypeIndex),expression:\"selectedTypeIndex\"}],staticClass:\"grd-row-col-1-6\",attrs:{\"type\":\"radio\"},domProps:{\"value\":i,\"checked\":_vm._q(_vm.selectedTypeIndex,i)},on:{\"change\":function($event){_vm.selectedTypeIndex=i}}}),_c('label',{staticClass:\"grd-row-col-5-6\",attrs:{\"for\":i}},[_vm._v(_vm._s(type))])])}))]],2)}\nvar staticRenderFns = []\n\nexport { render, staticRenderFns }","\n\n\n\n\n","import mod from \"-!../../../../node_modules/cache-loader/dist/cjs.js??ref--12-0!../../../../node_modules/thread-loader/dist/cjs.js!../../../../node_modules/babel-loader/lib/index.js!../../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./typepicker.vue?vue&type=script&lang=js\"; export default mod; export * from \"-!../../../../node_modules/cache-loader/dist/cjs.js??ref--12-0!../../../../node_modules/thread-loader/dist/cjs.js!../../../../node_modules/babel-loader/lib/index.js!../../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./typepicker.vue?vue&type=script&lang=js\"","import { render, staticRenderFns } from \"./typepicker.vue?vue&type=template&id=4a1aa84c&lang=pug\"\nimport script from \"./typepicker.vue?vue&type=script&lang=js\"\nexport * from \"./typepicker.vue?vue&type=script&lang=js\"\nimport style0 from \"./typepicker.vue?vue&type=style&index=0&lang=css\"\n\n\n/* normalize component */\nimport normalizer from \"!../../../../node_modules/vue-loader/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n null,\n null\n \n)\n\nexport default component.exports","\n\n\n\n\n","import mod from \"-!../../../node_modules/cache-loader/dist/cjs.js??ref--12-0!../../../node_modules/thread-loader/dist/cjs.js!../../../node_modules/babel-loader/lib/index.js!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./gekkoConfigBuilder.vue?vue&type=script&lang=js\"; export default mod; export * from \"-!../../../node_modules/cache-loader/dist/cjs.js??ref--12-0!../../../node_modules/thread-loader/dist/cjs.js!../../../node_modules/babel-loader/lib/index.js!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./gekkoConfigBuilder.vue?vue&type=script&lang=js\"","import { render, staticRenderFns } from \"./gekkoConfigBuilder.vue?vue&type=template&id=6015a1e6&lang=pug\"\nimport script from \"./gekkoConfigBuilder.vue?vue&type=script&lang=js\"\nexport * from \"./gekkoConfigBuilder.vue?vue&type=script&lang=js\"\nimport style0 from \"./gekkoConfigBuilder.vue?vue&type=style&index=0&lang=css\"\n\n\n/* normalize component */\nimport normalizer from \"!../../../node_modules/vue-loader/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n null,\n null\n \n)\n\nexport default component.exports","\n\n\n\n\n","import mod from \"-!../../../node_modules/cache-loader/dist/cjs.js??ref--12-0!../../../node_modules/thread-loader/dist/cjs.js!../../../node_modules/babel-loader/lib/index.js!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./new.vue?vue&type=script&lang=js\"; export default mod; export * from \"-!../../../node_modules/cache-loader/dist/cjs.js??ref--12-0!../../../node_modules/thread-loader/dist/cjs.js!../../../node_modules/babel-loader/lib/index.js!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./new.vue?vue&type=script&lang=js\"","import { render, staticRenderFns } from \"./new.vue?vue&type=template&id=da0dc488&lang=pug\"\nimport script from \"./new.vue?vue&type=script&lang=js\"\nexport * from \"./new.vue?vue&type=script&lang=js\"\nimport style0 from \"./new.vue?vue&type=style&index=0&lang=css\"\n\n\n/* normalize component */\nimport normalizer from \"!../../../node_modules/vue-loader/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n null,\n null\n \n)\n\nexport default component.exports","import mod from \"-!../../../node_modules/mini-css-extract-plugin/dist/loader.js!../../../node_modules/css-loader/index.js??ref--6-oneOf-1-1!../../../node_modules/vue-loader/lib/loaders/stylePostLoader.js!../../../node_modules/postcss-loader/lib/index.js??ref--6-oneOf-1-2!../../../node_modules/cache-loader/dist/cjs.js??ref--0-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./data.vue?vue&type=style&index=0&lang=css\"; export default mod; export * from \"-!../../../node_modules/mini-css-extract-plugin/dist/loader.js!../../../node_modules/css-loader/index.js??ref--6-oneOf-1-1!../../../node_modules/vue-loader/lib/loaders/stylePostLoader.js!../../../node_modules/postcss-loader/lib/index.js??ref--6-oneOf-1-2!../../../node_modules/cache-loader/dist/cjs.js??ref--0-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./data.vue?vue&type=style&index=0&lang=css\"","const marked = require('marked');\n\n// add `target='_blank'` to outgoing links\n\n// https://github.com/chjj/marked/pull/451#issuecomment-49976076\n\nvar myRenderer = new marked.Renderer();\nmyRenderer.link = function(href, title, text) {\n var external, newWindow, out;\n external = /^https?:\\/\\/.+$/.test(href);\n newWindow = external || title === 'newWindow';\n out = \"\" + text + \"\";\n};\n\nmarked.setOptions({renderer: myRenderer});\n\nexport default marked;","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',[_c('div',{staticClass:\"mx1\"},[_c('label',{staticClass:\"wrapper\",attrs:{\"for\":\"exchange\"}},[_vm._v(\"Exchange:\")]),_c('div',{staticClass:\"custom-select button\"},[_c('select',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.exchange),expression:\"exchange\"}],on:{\"change\":function($event){var $$selectedVal = Array.prototype.filter.call($event.target.options,function(o){return o.selected}).map(function(o){var val = \"_value\" in o ? o._value : o.value;return val}); _vm.exchange=$event.target.multiple ? $$selectedVal : $$selectedVal[0]}}},_vm._l((_vm.exchanges),function(market,e){return _c('option',[_vm._v(_vm._s(e))])}))])]),_c('div',{staticClass:\"grd-row\"},[_c('div',{staticClass:\"grd-row-col-3-6 mx1\"},[_c('label',{attrs:{\"for\":\"currency\"}},[_vm._v(\"Currency:\")]),_c('div',{staticClass:\"custom-select button\"},[_c('select',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.currency),expression:\"currency\"}],on:{\"change\":function($event){var $$selectedVal = Array.prototype.filter.call($event.target.options,function(o){return o.selected}).map(function(o){var val = \"_value\" in o ? o._value : o.value;return val}); _vm.currency=$event.target.multiple ? $$selectedVal : $$selectedVal[0]}}},_vm._l((_vm.currencies),function(cur){return _c('option',[_vm._v(_vm._s(cur))])}))])]),_c('div',{staticClass:\"grd-row-col-3-6 mx1\"},[_c('label',{attrs:{\"for\":\"asset\"}},[_vm._v(\"Asset:\")]),_c('div',{staticClass:\"custom-select button\"},[_c('select',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.asset),expression:\"asset\"}],on:{\"change\":function($event){var $$selectedVal = Array.prototype.filter.call($event.target.options,function(o){return o.selected}).map(function(o){var val = \"_value\" in o ? o._value : o.value;return val}); _vm.asset=$event.target.multiple ? $$selectedVal : $$selectedVal[0]}}},_vm._l((_vm.assets),function(asst){return _c('option',[_vm._v(_vm._s(asst))])}))])])])])}\nvar staticRenderFns = []\n\nexport { render, staticRenderFns }","\n\n\n\n","import mod from \"-!../../../../node_modules/cache-loader/dist/cjs.js??ref--12-0!../../../../node_modules/thread-loader/dist/cjs.js!../../../../node_modules/babel-loader/lib/index.js!../../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./marketpicker.vue?vue&type=script&lang=js\"; export default mod; export * from \"-!../../../../node_modules/cache-loader/dist/cjs.js??ref--12-0!../../../../node_modules/thread-loader/dist/cjs.js!../../../../node_modules/babel-loader/lib/index.js!../../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./marketpicker.vue?vue&type=script&lang=js\"","import { render, staticRenderFns } from \"./marketpicker.vue?vue&type=template&id=df4a8b2a&lang=pug\"\nimport script from \"./marketpicker.vue?vue&type=script&lang=js\"\nexport * from \"./marketpicker.vue?vue&type=script&lang=js\"\n\n\n/* normalize component */\nimport normalizer from \"!../../../../node_modules/vue-loader/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n null,\n null\n \n)\n\nexport default component.exports","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"grd\"},[_c('div',{staticClass:\"grd-row\"},[_c('div',{staticClass:\"grd-row-col-3-6 px1\"},[_c('h3',[_vm._v(\"Strategy\")]),_c('div',[_c('label',{staticClass:\"wrapper\",attrs:{\"for\":\"strat\"}},[_vm._v(\"Strategy:\")]),_c('div',{staticClass:\"custom-select button\"},[_c('select',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.strategy),expression:\"strategy\"}],on:{\"change\":function($event){var $$selectedVal = Array.prototype.filter.call($event.target.options,function(o){return o.selected}).map(function(o){var val = \"_value\" in o ? o._value : o.value;return val}); _vm.strategy=$event.target.multiple ? $$selectedVal : $$selectedVal[0]}}},_vm._l((_vm.strategies),function(strat){return _c('option',[_vm._v(_vm._s(strat.name))])}))])]),_c('div',[_c('label',{attrs:{\"for\":\"candleSize\"}},[_vm._v(\"Candle Size\")]),_c('div',{staticClass:\"grd-row\"},[_c('div',{staticClass:\"grd-row-col-3-6\"},[_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.rawCandleSize),expression:\"rawCandleSize\"}],domProps:{\"value\":(_vm.rawCandleSize)},on:{\"input\":function($event){if($event.target.composing){ return; }_vm.rawCandleSize=$event.target.value}}})]),_c('div',{staticClass:\"grd-row-col-3-6 align\"},[_c('div',{staticClass:\"custom-select button\"},[_c('select',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.candleSizeUnit),expression:\"candleSizeUnit\"}],on:{\"change\":function($event){var $$selectedVal = Array.prototype.filter.call($event.target.options,function(o){return o.selected}).map(function(o){var val = \"_value\" in o ? o._value : o.value;return val}); _vm.candleSizeUnit=$event.target.multiple ? $$selectedVal : $$selectedVal[0]}}},[_c('option',[_vm._v(\"minutes\")]),_c('option',[_vm._v(\"hours\")]),_c('option',[_vm._v(\"days\")])])])])])]),_c('div',[_c('label',{attrs:{\"for\":\"historySize\"}},[_vm._v(\"Warmup period (in \"+_vm._s(_vm.rawCandleSize)+\" \"+_vm._s(_vm.singularCandleSizeUnit)+\" candles):\")]),_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.historySize),expression:\"historySize\"}],domProps:{\"value\":(_vm.historySize)},on:{\"input\":function($event){if($event.target.composing){ return; }_vm.historySize=$event.target.value}}}),_c('em',{staticClass:\"label-like\"},[_vm._v(\"(will use \"+_vm._s(_vm.humanizeDuration(_vm.candleSize * _vm.historySize * 1000 * 60))+\" of data as history)\")])])]),_c('div',{staticClass:\"grd-row-col-3-6 px1\"},[_c('div',[_c('h3',[_vm._v(\"Parameters\")]),_c('p',[_vm._v(_vm._s(_vm.strategy)+\" Parameters:\")]),_c('textarea',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.rawStratParams),expression:\"rawStratParams\"}],staticClass:\"params\",domProps:{\"value\":(_vm.rawStratParams)},on:{\"input\":function($event){if($event.target.composing){ return; }_vm.rawStratParams=$event.target.value}}}),(_vm.rawStratParamsError)?_c('p',{staticClass:\"bg--red p1\"},[_vm._v(_vm._s(_vm.rawStratParamsError.message))]):_vm._e()])])])])}\nvar staticRenderFns = []\n\nexport { render, staticRenderFns }","\n\n\n\n","import mod from \"-!../../../../node_modules/cache-loader/dist/cjs.js??ref--12-0!../../../../node_modules/thread-loader/dist/cjs.js!../../../../node_modules/babel-loader/lib/index.js!../../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./stratpicker.vue?vue&type=script&lang=js\"; export default mod; export * from \"-!../../../../node_modules/cache-loader/dist/cjs.js??ref--12-0!../../../../node_modules/thread-loader/dist/cjs.js!../../../../node_modules/babel-loader/lib/index.js!../../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./stratpicker.vue?vue&type=script&lang=js\"","import { render, staticRenderFns } from \"./stratpicker.vue?vue&type=template&id=1df710ae&lang=pug\"\nimport script from \"./stratpicker.vue?vue&type=script&lang=js\"\nexport * from \"./stratpicker.vue?vue&type=script&lang=js\"\nimport style0 from \"./stratpicker.vue?vue&type=style&index=0&lang=css\"\n\n\n/* normalize component */\nimport normalizer from \"!../../../../node_modules/vue-loader/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n null,\n null\n \n)\n\nexport default component.exports","import mod from \"-!../../../../node_modules/mini-css-extract-plugin/dist/loader.js!../../../../node_modules/css-loader/index.js??ref--6-oneOf-1-1!../../../../node_modules/vue-loader/lib/loaders/stylePostLoader.js!../../../../node_modules/postcss-loader/lib/index.js??ref--6-oneOf-1-2!../../../../node_modules/cache-loader/dist/cjs.js??ref--0-0!../../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./rangepicker.vue?vue&type=style&index=0&lang=css\"; export default mod; export * from \"-!../../../../node_modules/mini-css-extract-plugin/dist/loader.js!../../../../node_modules/css-loader/index.js??ref--6-oneOf-1-1!../../../../node_modules/vue-loader/lib/loaders/stylePostLoader.js!../../../../node_modules/postcss-loader/lib/index.js??ref--6-oneOf-1-2!../../../../node_modules/cache-loader/dist/cjs.js??ref--0-0!../../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./rangepicker.vue?vue&type=style&index=0&lang=css\"","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',[_c('h3',[_vm._v(\"Daterange\")]),_c('div',[_c('label',{attrs:{\"for\":\"from\"}},[_vm._v(\"From\")]),_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.from),expression:\"from\"}],domProps:{\"value\":(_vm.from)},on:{\"input\":function($event){if($event.target.composing){ return; }_vm.from=$event.target.value}}})]),_c('div',[_c('label',{attrs:{\"for\":\"to\"}},[_vm._v(\"To\")]),_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.to),expression:\"to\"}],domProps:{\"value\":(_vm.to)},on:{\"input\":function($event){if($event.target.composing){ return; }_vm.to=$event.target.value}}})])])}\nvar staticRenderFns = []\n\nexport { render, staticRenderFns }","\n\n\n\n\n","import mod from \"-!../../../../node_modules/cache-loader/dist/cjs.js??ref--12-0!../../../../node_modules/thread-loader/dist/cjs.js!../../../../node_modules/babel-loader/lib/index.js!../../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./rangecreator.vue?vue&type=script&lang=js\"; export default mod; export * from \"-!../../../../node_modules/cache-loader/dist/cjs.js??ref--12-0!../../../../node_modules/thread-loader/dist/cjs.js!../../../../node_modules/babel-loader/lib/index.js!../../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./rangecreator.vue?vue&type=script&lang=js\"","import { render, staticRenderFns } from \"./rangecreator.vue?vue&type=template&id=8c51143a&lang=pug\"\nimport script from \"./rangecreator.vue?vue&type=script&lang=js\"\nexport * from \"./rangecreator.vue?vue&type=script&lang=js\"\nimport style0 from \"./rangecreator.vue?vue&type=style&index=0&lang=css\"\n\n\n/* normalize component */\nimport normalizer from \"!../../../../node_modules/vue-loader/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n null,\n null\n \n)\n\nexport default component.exports","import mod from \"-!../../../../node_modules/mini-css-extract-plugin/dist/loader.js!../../../../node_modules/css-loader/index.js??ref--6-oneOf-1-1!../../../../node_modules/vue-loader/lib/loaders/stylePostLoader.js!../../../../node_modules/postcss-loader/lib/index.js??ref--6-oneOf-1-2!../../../../node_modules/cache-loader/dist/cjs.js??ref--0-0!../../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./importConfigBuilder.vue?vue&type=style&index=0&lang=css\"; export default mod; export * from \"-!../../../../node_modules/mini-css-extract-plugin/dist/loader.js!../../../../node_modules/css-loader/index.js??ref--6-oneOf-1-1!../../../../node_modules/vue-loader/lib/loaders/stylePostLoader.js!../../../../node_modules/postcss-loader/lib/index.js??ref--6-oneOf-1-2!../../../../node_modules/cache-loader/dist/cjs.js??ref--0-0!../../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./importConfigBuilder.vue?vue&type=style&index=0&lang=css\"","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _vm._m(0)}\nvar staticRenderFns = [function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"spinner\"},[_c('div',{staticClass:\"rect1\"}),_c('div',{staticClass:\"rect2\"}),_c('div',{staticClass:\"rect3\"}),_c('div',{staticClass:\"rect4\"})])}]\n\nexport { render, staticRenderFns }","// http://tobiasahlin.com/spinkit/\n\n\n\n\n\n\n","import mod from \"-!../../../node_modules/cache-loader/dist/cjs.js??ref--12-0!../../../node_modules/thread-loader/dist/cjs.js!../../../node_modules/babel-loader/lib/index.js!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./blockSpinner.vue?vue&type=script&lang=js\"; export default mod; export * from \"-!../../../node_modules/cache-loader/dist/cjs.js??ref--12-0!../../../node_modules/thread-loader/dist/cjs.js!../../../node_modules/babel-loader/lib/index.js!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./blockSpinner.vue?vue&type=script&lang=js\"","import { render, staticRenderFns } from \"./blockSpinner.vue?vue&type=template&id=45447ea6&lang=pug\"\nimport script from \"./blockSpinner.vue?vue&type=script&lang=js\"\nexport * from \"./blockSpinner.vue?vue&type=script&lang=js\"\nimport style0 from \"./blockSpinner.vue?vue&type=style&index=0&lang=css\"\n\n\n/* normalize component */\nimport normalizer from \"!../../../node_modules/vue-loader/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n null,\n null\n \n)\n\nexport default component.exports","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{attrs:{\"id\":\"app\"}},[_c('top'),_c('div',{staticClass:\"fill\"},[_c('router-view',{staticClass:\"view\"})],1),_c('bottom'),_c('modal')],1)}\nvar staticRenderFns = []\n\nexport { render, staticRenderFns }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',[_c('div',{attrs:{\"id\":\"top\"}}),_vm._m(0),_c('nav',{staticClass:\"bg--light-gray\"},[_c('div',{staticClass:\"menu contain\"},[_c('router-link',{staticClass:\"py1\",attrs:{\"to\":\"/home\"}},[_vm._v(\"Home\")]),_c('router-link',{staticClass:\"py1\",attrs:{\"to\":\"/live-gekkos\"}},[_vm._v(\"Live Gekkos\")]),_c('router-link',{staticClass:\"py1\",attrs:{\"to\":\"/backtest\"}},[_vm._v(\"Backtest\")]),_c('router-link',{staticClass:\"py1\",attrs:{\"to\":\"/data\"}},[_vm._v(\"Local data\")]),_c('router-link',{staticClass:\"py1\",attrs:{\"to\":\"/config\"}},[_vm._v(\"Config\")]),_c('a',{staticClass:\"py1\",attrs:{\"href\":\"https://gekko.wizb.it/docs/introduction/about_gekko.html\",\"target\":\"_blank\"}},[_vm._v(\"Documentation\")])],1)])])}\nvar staticRenderFns = [function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('header',{staticClass:\"bg--off-white grd\"},[_c('div',{staticClass:\"contain grd-row\"},[_c('h3',{staticClass:\"py1 px2 col-2\"},[_vm._v(\"Gekko UI\")])])])}]\n\nexport { render, staticRenderFns }","\n\n\n\n\n","import mod from \"-!../../../node_modules/cache-loader/dist/cjs.js??ref--12-0!../../../node_modules/thread-loader/dist/cjs.js!../../../node_modules/babel-loader/lib/index.js!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./header.vue?vue&type=script&lang=js\"; export default mod; export * from \"-!../../../node_modules/cache-loader/dist/cjs.js??ref--12-0!../../../node_modules/thread-loader/dist/cjs.js!../../../node_modules/babel-loader/lib/index.js!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./header.vue?vue&type=script&lang=js\"","import { render, staticRenderFns } from \"./header.vue?vue&type=template&id=0a62d360&lang=pug\"\nimport script from \"./header.vue?vue&type=script&lang=js\"\nexport * from \"./header.vue?vue&type=script&lang=js\"\nimport style0 from \"./header.vue?vue&type=style&index=0&lang=css\"\n\n\n/* normalize component */\nimport normalizer from \"!../../../node_modules/vue-loader/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n null,\n null\n \n)\n\nexport default component.exports","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('footer',{staticClass:\"p2 bg--off-white\"},[_c('div',{staticClass:\"contain\"},[_vm._m(0),_c('p',[_vm._v(\"Using Gekko v\"+_vm._s(_vm.version.gekko)+\" and Gekko UI v\"+_vm._s(_vm.version.ui)+\".\")])])])}\nvar staticRenderFns = [function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('p',[_c('em',[_vm._v(\"Use Gekko at your own risk.\")])])}]\n\nexport { render, staticRenderFns }","\n\n\n","import mod from \"-!../../../node_modules/cache-loader/dist/cjs.js??ref--12-0!../../../node_modules/thread-loader/dist/cjs.js!../../../node_modules/babel-loader/lib/index.js!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./footer.vue?vue&type=script&lang=js\"; export default mod; export * from \"-!../../../node_modules/cache-loader/dist/cjs.js??ref--12-0!../../../node_modules/thread-loader/dist/cjs.js!../../../node_modules/babel-loader/lib/index.js!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./footer.vue?vue&type=script&lang=js\"","import { render, staticRenderFns } from \"./footer.vue?vue&type=template&id=7782dc11&lang=pug\"\nimport script from \"./footer.vue?vue&type=script&lang=js\"\nexport * from \"./footer.vue?vue&type=script&lang=js\"\n\n\n/* normalize component */\nimport normalizer from \"!../../../node_modules/vue-loader/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n null,\n null\n \n)\n\nexport default component.exports","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return (_vm.active)?_c('div',[_c('div',{attrs:{\"id\":\"modal-background\"}}),_c('div',{staticClass:\"modal\",attrs:{\"id\":\"modal\"}},[_c('div',{staticClass:\"modal-guts\",domProps:{\"innerHTML\":_vm._s(_vm.content)}})])]):_vm._e()}\nvar staticRenderFns = []\n\nexport { render, staticRenderFns }","\n\n\n\n\n","import mod from \"-!../../../node_modules/cache-loader/dist/cjs.js??ref--12-0!../../../node_modules/thread-loader/dist/cjs.js!../../../node_modules/babel-loader/lib/index.js!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./modal.vue?vue&type=script&lang=js\"; export default mod; export * from \"-!../../../node_modules/cache-loader/dist/cjs.js??ref--12-0!../../../node_modules/thread-loader/dist/cjs.js!../../../node_modules/babel-loader/lib/index.js!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./modal.vue?vue&type=script&lang=js\"","import { render, staticRenderFns } from \"./modal.vue?vue&type=template&id=340a7d17&lang=pug\"\nimport script from \"./modal.vue?vue&type=script&lang=js\"\nexport * from \"./modal.vue?vue&type=script&lang=js\"\nimport style0 from \"./modal.vue?vue&type=style&index=0&lang=css\"\n\n\n/* normalize component */\nimport normalizer from \"!../../../node_modules/vue-loader/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n null,\n null\n \n)\n\nexport default component.exports","\n\n\n\n\n","import mod from \"-!../node_modules/cache-loader/dist/cjs.js??ref--12-0!../node_modules/thread-loader/dist/cjs.js!../node_modules/babel-loader/lib/index.js!../node_modules/vue-loader/lib/index.js??vue-loader-options!./App.vue?vue&type=script&lang=js\"; export default mod; export * from \"-!../node_modules/cache-loader/dist/cjs.js??ref--12-0!../node_modules/thread-loader/dist/cjs.js!../node_modules/babel-loader/lib/index.js!../node_modules/vue-loader/lib/index.js??vue-loader-options!./App.vue?vue&type=script&lang=js\"","import { render, staticRenderFns } from \"./App.vue?vue&type=template&id=29e7b8c2&lang=pug\"\nimport script from \"./App.vue?vue&type=script&lang=js\"\nexport * from \"./App.vue?vue&type=script&lang=js\"\nimport style0 from \"./App.vue?vue&type=style&index=0&lang=css\"\n\n\n/* normalize component */\nimport normalizer from \"!../node_modules/vue-loader/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n null,\n null\n \n)\n\nexport default component.exports","import Vue from 'vue'\n\nexport const addImport = (state, imp) => {\n state.imports.push(imp);\n return state;\n}\n\nexport const syncImports = (state, imports) => {\n state.imports = imports;\n return state;\n}\n\nexport const updateImport = (state, update) => {\n let index = state.imports.findIndex(i => i.id === update.import_id);\n let item = state.imports[index];\n if(!item)\n return state;\n\n let updated = Vue.util.extend(item, update.updates);\n Vue.set(state.imports, index, updated);\n\n return state;\n}","// Redux/vuex inspired reducer, reduces an event into a gekko state.\n// NOTE: this is used by the backend as well as the frontend.\n\nconst skipInitialEvents = ['marketUpdate'];\nconst skipLatestEvents = ['marketStart', 'stratWarmupCompleted'];\nconst trackAllEvents = ['tradeCompleted', 'advice', 'roundtrip'];\n\nconst reduce = (state, event) => {\n const type = event.type;\n const payload = event.payload;\n\n state = {\n ...state,\n latestUpdate: new Date()\n }\n\n if(trackAllEvents.includes(type)) {\n if(!state.events[type]) {\n state = {\n ...state,\n events: {\n ...state.events,\n [type]: [ payload ]\n }\n }\n } else {\n state = {\n ...state,\n events: {\n ...state.events,\n [type]: [ ...state.events[type], payload ]\n }\n }\n }\n }\n\n if(!state.events.initial[type] && !skipInitialEvents.includes(type)) {\n state = {\n ...state,\n events: {\n ...state.events,\n initial: {\n ...state.events.initial,\n [type]: payload\n }\n }\n }\n }\n\n if(!skipLatestEvents.includes(type)) {\n state = {\n ...state,\n events: {\n ...state.events,\n latest: {\n ...state.events.latest,\n [type]: payload\n }\n }\n }\n }\n\n return state;\n}\n\n// export default reduce;\nmodule.exports = reduce;","import Vue from 'vue'\nimport _ from 'lodash';\nconst reduceState = require('../../../../../state/reduceState');\n\nexport const syncGekkos = (state, data) => {\n if(!data) {\n return state;\n }\n\n state.gekkos = data.live;\n state.archivedGekkos = data.archive;\n return state;\n}\n\nexport const addGekko = (state, gekko) => {\n state.gekkos = {\n ...state.gekkos,\n [gekko.id]: gekko\n }\n return state;\n}\n\nexport const updateGekko = (state, update) => {\n if(!update.id || !_.has(state.gekkos, update.id)) {\n return console.error('cannot update unknown gekko..');;\n }\n\n state.gekkos = {\n ...state.gekkos,\n [update.id]: reduceState(state.gekkos[update.id], update.event)\n }\n return state;\n}\n\nexport const archiveGekko = (state, id) => {\n if(!_.has(state.gekkos, id)) {\n return console.error('cannot archive unknown gekko..');\n }\n\n state.archivedGekkos = {\n ...state.archivedGekkos,\n [id]: {\n ...state.gekkos[id],\n stopped: true,\n active: false\n }\n }\n\n state.gekkos = _.omit(state.gekkos, id);\n return state;\n}\n\nexport const errorGekko = (state, data) => {\n if(!_.has(state.gekkos, data.id)) {\n return console.error('cannot error unknown gekko..');\n }\n\n state.gekkos = {\n ...state.gekkos,\n [data.id]: {\n ...state.gekkos[data.id],\n errored: true,\n errorMessage: data.error\n }\n }\n\n return state;\n}\n\nexport const deleteGekko = (state, id) => {\n if(!_.has(state.archivedGekkos, id)) {\n return console.error('cannot delete unknown gekko..');\n }\n\n state.archivedGekkos = _.omit(state.archivedGekkos, id);\n return state;\n}","import Vue from 'vue'\nimport _ from 'lodash'\n\nexport const setGlobalWarning = (state, warning) => {\n state.warnings[warning.key] = warning.value;\n return state;\n}","import Vue from 'vue'\n\nexport const syncApiKeys = (state, apiKeys) => {\n Vue.set(state, 'apiKeys', apiKeys);\n return state;\n}\n\nexport const syncExchanges = (state, exchanges) => {\n Vue.set(state, 'exchanges', exchanges);\n return state;\n}","import Vue from 'vue'\nimport Vuex from 'vuex'\nimport _ from 'lodash'\n\nimport * as importMutations from './modules/imports/mutations'\nimport * as gekkoMutations from './modules/gekkos/mutations'\nimport * as notificationMutations from './modules/notifications/mutations'\nimport * as configMutations from './modules/config/mutations'\n\nVue.use(Vuex);\n\nconst debug = process.env.NODE_ENV !== 'production'\n\nlet mutations = {};\n\n_.merge(mutations, importMutations);\n_.merge(mutations, gekkoMutations);\n_.merge(mutations, notificationMutations);\n_.merge(mutations, configMutations);\n\nexport default new Vuex.Store({\n state: {\n warnings: {\n connected: true, // assume we will connect\n },\n imports: [],\n gekkos: {},\n archivedGekkos: {},\n connection: {\n disconnected: false,\n reconnected: false\n },\n apiKeys: [],\n exchanges: {}\n },\n mutations,\n strict: debug\n})","import mod from \"-!../../../node_modules/mini-css-extract-plugin/dist/loader.js!../../../node_modules/css-loader/index.js??ref--6-oneOf-1-1!../../../node_modules/vue-loader/lib/loaders/stylePostLoader.js!../../../node_modules/postcss-loader/lib/index.js??ref--6-oneOf-1-2!../../../node_modules/cache-loader/dist/cjs.js??ref--0-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./backtestConfigBuilder.vue?vue&type=style&index=0&lang=css\"; export default mod; export * from \"-!../../../node_modules/mini-css-extract-plugin/dist/loader.js!../../../node_modules/css-loader/index.js??ref--6-oneOf-1-1!../../../node_modules/vue-loader/lib/loaders/stylePostLoader.js!../../../node_modules/postcss-loader/lib/index.js??ref--6-oneOf-1-2!../../../node_modules/cache-loader/dist/cjs.js??ref--0-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./backtestConfigBuilder.vue?vue&type=style&index=0&lang=css\"","import mod from \"-!../../../../node_modules/mini-css-extract-plugin/dist/loader.js!../../../../node_modules/css-loader/index.js??ref--6-oneOf-1-1!../../../../node_modules/vue-loader/lib/loaders/stylePostLoader.js!../../../../node_modules/postcss-loader/lib/index.js??ref--6-oneOf-1-2!../../../../node_modules/cache-loader/dist/cjs.js??ref--0-0!../../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./chartWrapper.vue?vue&type=style&index=0&lang=css\"; export default mod; export * from \"-!../../../../node_modules/mini-css-extract-plugin/dist/loader.js!../../../../node_modules/css-loader/index.js??ref--6-oneOf-1-1!../../../../node_modules/vue-loader/lib/loaders/stylePostLoader.js!../../../../node_modules/postcss-loader/lib/index.js??ref--6-oneOf-1-2!../../../../node_modules/cache-loader/dist/cjs.js??ref--0-0!../../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./chartWrapper.vue?vue&type=style&index=0&lang=css\"","import mod from \"-!../../../../node_modules/mini-css-extract-plugin/dist/loader.js!../../../../node_modules/css-loader/index.js??ref--6-oneOf-1-1!../../../../node_modules/vue-loader/lib/loaders/stylePostLoader.js!../../../../node_modules/postcss-loader/lib/index.js??ref--6-oneOf-1-2!../../../../node_modules/cache-loader/dist/cjs.js??ref--0-0!../../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./rangecreator.vue?vue&type=style&index=0&lang=css\"; export default mod; export * from \"-!../../../../node_modules/mini-css-extract-plugin/dist/loader.js!../../../../node_modules/css-loader/index.js??ref--6-oneOf-1-1!../../../../node_modules/vue-loader/lib/loaders/stylePostLoader.js!../../../../node_modules/postcss-loader/lib/index.js??ref--6-oneOf-1-2!../../../../node_modules/cache-loader/dist/cjs.js??ref--0-0!../../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./rangecreator.vue?vue&type=style&index=0&lang=css\"","import mod from \"-!../../../node_modules/mini-css-extract-plugin/dist/loader.js!../../../node_modules/css-loader/index.js??ref--6-oneOf-1-1!../../../node_modules/vue-loader/lib/loaders/stylePostLoader.js!../../../node_modules/postcss-loader/lib/index.js??ref--6-oneOf-1-2!../../../node_modules/cache-loader/dist/cjs.js??ref--0-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./modal.vue?vue&type=style&index=0&lang=css\"; export default mod; export * from \"-!../../../node_modules/mini-css-extract-plugin/dist/loader.js!../../../node_modules/css-loader/index.js??ref--6-oneOf-1-1!../../../node_modules/vue-loader/lib/loaders/stylePostLoader.js!../../../node_modules/postcss-loader/lib/index.js??ref--6-oneOf-1-2!../../../node_modules/cache-loader/dist/cjs.js??ref--0-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./modal.vue?vue&type=style&index=0&lang=css\"","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{class:{ clickable: !_vm.isClicked },attrs:{\"id\":\"chartWrapper\"}},[_c('div',{staticClass:\"shield\",on:{\"click\":function($event){$event.preventDefault();return _vm.click($event)}}}),_c('svg',{attrs:{\"id\":\"chart\",\"width\":\"960\",\"height\":_vm.height}})])}\nvar staticRenderFns = []\n\nexport { render, staticRenderFns }","import _ from 'lodash';\n// global moment\n\nexport default function(_data, _trades, _height) {\n\n const toDate = i => {\n if(_.isNumber(i)) {\n return moment.unix(i).utc().toDate();\n } else {\n return moment.utc(i).toDate();\n }\n }\n\n const trades = _trades.map(t => {\n return {\n price: t.price,\n date: toDate(t.date),\n action: t.action\n }\n });\n\n const data = _data.map(c => {\n return {\n price: c.open,\n date: toDate(c.start)\n }\n });\n\n var dates = data.map(c => +c.date);\n var prices = data.map(c => +c.price)\n\n var svg = d3.select(\"#chart\");\n\n svg.attr(\"width\", window.innerWidth - 20);\n\n var margin = {top: 20, right: 20, bottom: 110, left: 40};\n var height = _height - margin.top - margin.bottom;\n var margin2 = {top: _height - 70, right: 20, bottom: 30, left: 40};\n var width = +svg.attr(\"width\") - margin.left - margin.right;\n var height2 = _height - margin2.top - margin2.bottom;\n\n var x = d3.scaleUtc().range([0, width]),\n x2 = d3.scaleUtc().range([0, width]),\n y = d3.scaleLinear().range([height, 0]),\n y2 = d3.scaleLinear().range([height2, 0]);\n\n var xAxis = d3.axisBottom(x),\n xAxis2 = d3.axisBottom(x2),\n yAxis = d3.axisLeft(y).ticks(_height / 50);\n\n var brush = d3.brushX()\n .extent([[0, 0], [width, height2]])\n .on(\"brush end\", brushed);\n\n var zoom = d3.zoom()\n .scaleExtent([1, 100])\n .translateExtent([[0, 0], [width, height]])\n .extent([[0, 0], [width, height]])\n .on(\"zoom\", zoomed);\n\n var line = d3.line()\n .x(function(d) { return x(d.date); })\n .y(function(d) { return y(d.price); });\n\n var line2 = d3.line()\n .x(function(d) { return x2(d.date); })\n .y(function(d) { return y2(d.price); });\n\n svg.append(\"defs\").append(\"clipPath\")\n .attr(\"id\", \"clip\")\n .append(\"rect\")\n .attr(\"width\", width)\n .attr(\"height\", height);\n\n var focus = svg.append(\"g\")\n .attr(\"class\", \"focus\")\n .attr(\"transform\", \"translate(\" + margin.left + \",\" + margin.top + \")\");\n\n var context = svg.append(\"g\")\n .attr(\"class\", \"context\")\n .attr(\"transform\", \"translate(\" + margin2.left + \",\" + margin2.top + \")\");\n\n x.domain(d3.extent(data, function(d) { return d.date; }));\n y.domain([\n d3.min(prices) * 0.99,\n d3.max(prices) * 1.01\n ]);\n x2.domain(x.domain());\n y2.domain(y.domain());\n\n focus.append(\"path\")\n .datum(data)\n .attr(\"class\", \"line price\")\n .attr(\"d\", line);\n\n focus.append(\"g\")\n .attr(\"class\", \"axis axis--x\")\n .attr(\"transform\", \"translate(0,\" + height + \")\")\n .call(xAxis);\n\n focus.append(\"g\")\n .attr(\"class\", \"axis axis--y\")\n .call(yAxis);\n\n context.append(\"path\")\n .datum(data)\n .attr(\"class\", \"line\")\n .attr(\"d\", line2);\n\n context.append(\"g\")\n .attr(\"class\", \"axis axis--x\")\n .attr(\"transform\", \"translate(0,\" + height2 + \")\")\n .call(xAxis2);\n\n var circles = svg\n .append('g')\n .attr(\"transform\", \"translate(\" + margin.left + \",\" + margin.top + \")\")\n .selectAll(\"circle\")\n .data(trades)\n .enter().append(\"circle\")\n .attr('class', function(d) { return d.action })\n .attr(\"cx\", function(d) { return x(d.date); })\n .attr(\"cy\", function(d) { return y(d.price); })\n .attr('r', 5);\n\n var brushCircles = context\n .append('g')\n // .attr(\"transform\", \"translate(\" + margin.left + \",\" + margin.top + \")\")\n .selectAll(\"circle\")\n .data(trades)\n .enter().append(\"circle\")\n .attr('class', function(d) { return d.action })\n .attr(\"cx\", function(d) { return x2(d.date); })\n .attr(\"cy\", function(d) { return y2(d.price); })\n .attr('r', 3);\n\n\n context.append(\"g\")\n .attr(\"class\", \"brush\")\n .call(brush)\n .call(brush.move, x.range());\n\n svg.append(\"rect\")\n .attr(\"class\", \"zoom\")\n .attr(\"width\", width)\n .attr(\"height\", height)\n .attr(\"transform\", \"translate(\" + margin.left + \",\" + margin.top + \")\")\n .call(zoom);\n\n function brushed() {\n if (d3.event.sourceEvent && d3.event.sourceEvent.type === \"zoom\") return; // ignore brush-by-zoom\n var s = d3.event.selection || x2.range();\n x.domain(s.map(x2.invert, x2));\n\n scaleY(x.domain());\n\n svg.select(\".axis--y\")\n .call(yAxis);\n\n circles\n .attr(\"cx\", function(d) { return x(d.date); })\n .attr(\"cy\", function(d) { return y(d.price); })\n\n focus.select(\".line\").attr(\"d\", line);\n focus.select(\".axis--x\").call(xAxis);\n svg.select(\".zoom\").call(zoom.transform, d3.zoomIdentity\n .scale(width / (s[1] - s[0]))\n .translate(-s[0], 0));\n }\n\n function scaleY(domain) {\n let [min, max] = domain;\n\n let minIndex = _.sortedIndex(dates, min);\n let maxIndex = _.sortedIndex(dates, max);\n\n let set = prices.slice(minIndex, maxIndex);\n y.domain([\n d3.min(set) * 0.9995,\n d3.max(set) * 1.0005\n ]);\n }\n\n function zoomed() {\n if (d3.event.sourceEvent && d3.event.sourceEvent.type === \"brush\") return; // ignore zoom-by-brush\n var t = d3.event.transform;\n\n scaleY(t.rescaleX(x2).domain()); \n\n svg.select(\".axis--y\")\n .call(yAxis);\n\n x.domain(t.rescaleX(x2).domain());\n focus.select(\".line\").attr(\"d\", line);\n\n circles\n .attr(\"cx\", function(d) { return x(d.date); })\n .attr(\"cy\", function(d) { return y(d.price); })\n\n\n focus.select(\".axis--x\").call(xAxis);\n context.select(\".brush\").call(brush.move, x.range().map(t.invertX, t));\n }\n}","export const draw = function(message) {\n d3.select(\"#chart\").append(\"text\")\n .attr('class', 'message')\n .attr('x', 150)\n .attr('y', 150)\n .text(message);\n}\n\nexport const clear = function() {\n d3.select(\"#chart\").find('text').remove();\n}","\n\n\n\n\n","import mod from \"-!../../../../node_modules/cache-loader/dist/cjs.js??ref--12-0!../../../../node_modules/thread-loader/dist/cjs.js!../../../../node_modules/babel-loader/lib/index.js!../../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./chartWrapper.vue?vue&type=script&lang=js\"; export default mod; export * from \"-!../../../../node_modules/cache-loader/dist/cjs.js??ref--12-0!../../../../node_modules/thread-loader/dist/cjs.js!../../../../node_modules/babel-loader/lib/index.js!../../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./chartWrapper.vue?vue&type=script&lang=js\"","import { render, staticRenderFns } from \"./chartWrapper.vue?vue&type=template&id=12bcb8d8&lang=pug\"\nimport script from \"./chartWrapper.vue?vue&type=script&lang=js\"\nexport * from \"./chartWrapper.vue?vue&type=script&lang=js\"\nimport style0 from \"./chartWrapper.vue?vue&type=style&index=0&lang=css\"\n\n\n/* normalize component */\nimport normalizer from \"!../../../../node_modules/vue-loader/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n null,\n null\n \n)\n\nexport default component.exports","import Vue from 'vue'\nimport App from './App.vue'\n\nimport VueRouter from 'vue-router'\nVue.use(VueRouter);\n\nimport store from './store'\n\nimport backtester from './components/backtester/backtester.vue'\nimport home from './components/layout/home.vue'\n\nimport data from './components/data/data.vue'\nimport importer from './components/data/import/importer.vue'\nimport singleImport from './components/data/import/single.vue'\nimport config from './components/config/config.vue'\n\nimport gekkoList from './components/gekko/list.vue'\nimport newGekko from './components/gekko/new.vue'\nimport singleGekko from './components/gekko/singleGekko.vue'\nimport { connect as connectWS } from './components/global/ws'\n\nconst router = new VueRouter({\n mode: 'hash',\n base: __dirname,\n routes: [\n { path: '/', redirect: '/home' },\n { path: '/home', component: home },\n { path: '/backtest', component: backtester },\n { path: '/config', component: config },\n { path: '/data', component: data },\n { path: '/data/importer', component: importer },\n { path: '/data/importer/import/:id', component: singleImport },\n { path: '/live-gekkos', component: gekkoList },\n { path: '/live-gekkos/new', component: newGekko },\n { path: '/live-gekkos/:id', component: singleGekko },\n ]\n});\n\n// setup some stuff\nconnectWS();\n\nnew Vue({\n router,\n store,\n el: '#app',\n render: h => h(App)\n})","import mod from \"-!../../../../node_modules/mini-css-extract-plugin/dist/loader.js!../../../../node_modules/css-loader/index.js??ref--6-oneOf-1-1!../../../../node_modules/vue-loader/lib/loaders/stylePostLoader.js!../../../../node_modules/postcss-loader/lib/index.js??ref--6-oneOf-1-2!../../../../node_modules/cache-loader/dist/cjs.js??ref--0-0!../../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./summary.vue?vue&type=style&index=0&lang=css\"; export default mod; export * from \"-!../../../../node_modules/mini-css-extract-plugin/dist/loader.js!../../../../node_modules/css-loader/index.js??ref--6-oneOf-1-1!../../../../node_modules/vue-loader/lib/loaders/stylePostLoader.js!../../../../node_modules/postcss-loader/lib/index.js??ref--6-oneOf-1-2!../../../../node_modules/cache-loader/dist/cjs.js??ref--0-0!../../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./summary.vue?vue&type=style&index=0&lang=css\"","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"contain\"},[_c('h2',[_vm._v(\"Config\")]),_c('div',{staticClass:\"hr\"}),_c('h3',[_vm._v(\"Available API keys\")]),(!_vm.apiKeySets.length)?_c('p',[_c('em',[_vm._v(\"You don't have any API keys yet.\")])]):_vm._e(),_c('ul',_vm._l((_vm.apiKeySets),function(exchange){return _c('li',[_vm._v(_vm._s(exchange)+\" (\"),_c('a',{attrs:{\"href\":\"#\"},on:{\"click\":function($event){$event.preventDefault();_vm.removeApiKey(exchange)}}},[_vm._v(\"remove\")]),_vm._v(\")\")])})),(!_vm.addApiToggle)?_c('a',{staticClass:\"btn--primary\",attrs:{\"href\":\"#\"},on:{\"click\":function($event){$event.preventDefault();return _vm.openAddApi($event)}}},[_vm._v(\"Add an API key\")]):_vm._e(),(_vm.addApiToggle)?[_c('div',{staticClass:\"hr\"}),_c('apiConfigBuilder')]:_vm._e(),_c('div',{staticClass:\"hr\"})],2)}\nvar staticRenderFns = []\n\nexport { render, staticRenderFns }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"grd contain\"},[_c('h3',[_vm._v(\"Add an API key\")]),_c('p',[_vm._v(\"Make sure that the API key has the permissions to create and cancel orders and view balances.\")]),_c('div',{staticClass:\"grd-row\"},[_c('div',{staticClass:\"grd-row-col-3-6 mx1\"},[_c('h3',[_vm._v(\"Exchange\")]),_c('exchange-picker',{attrs:{\"only-tradable\":\"true\"},on:{\"exchange\":_vm.updateExchange}})],1),_c('div',{staticClass:\"grd-row-col-3-6 mx1\"},[_c('h3',[_vm._v(\"Credentials\")]),_vm._l((_vm.requires),function(cred){return [_c('label',[_vm._v(_vm._s(cred))]),_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.credentials[cred]),expression:\"credentials[cred]\"}],domProps:{\"value\":(_vm.credentials[cred])},on:{\"input\":function($event){if($event.target.composing){ return; }_vm.$set(_vm.credentials, cred, $event.target.value)}}})]})],2)]),_c('div',{staticClass:\"txt--center\"},[_c('a',{staticClass:\"w100--s my1 btn--primary\",attrs:{\"href\":\"#\"},on:{\"click\":function($event){$event.preventDefault();return _vm.upload($event)}}},[_vm._v(\"Add\")])])])}\nvar staticRenderFns = []\n\nexport { render, staticRenderFns }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',[_c('div',{staticClass:\"mx1\"},[_c('label',{staticClass:\"wrapper\",attrs:{\"for\":\"exchange\"}},[_vm._v(\"Exchange:\")]),_c('div',{staticClass:\"custom-select button\"},[_c('select',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.exchange),expression:\"exchange\"}],on:{\"change\":function($event){var $$selectedVal = Array.prototype.filter.call($event.target.options,function(o){return o.selected}).map(function(o){var val = \"_value\" in o ? o._value : o.value;return val}); _vm.exchange=$event.target.multiple ? $$selectedVal : $$selectedVal[0]}}},_vm._l((_vm.exchanges),function(market,e){return _c('option',[_vm._v(_vm._s(e))])}))])])])}\nvar staticRenderFns = []\n\nexport { render, staticRenderFns }","\n\n\n\n","import mod from \"-!../../../../node_modules/cache-loader/dist/cjs.js??ref--12-0!../../../../node_modules/thread-loader/dist/cjs.js!../../../../node_modules/babel-loader/lib/index.js!../../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./exchangepicker.vue?vue&type=script&lang=js\"; export default mod; export * from \"-!../../../../node_modules/cache-loader/dist/cjs.js??ref--12-0!../../../../node_modules/thread-loader/dist/cjs.js!../../../../node_modules/babel-loader/lib/index.js!../../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./exchangepicker.vue?vue&type=script&lang=js\"","import { render, staticRenderFns } from \"./exchangepicker.vue?vue&type=template&id=50c29cb0&lang=pug\"\nimport script from \"./exchangepicker.vue?vue&type=script&lang=js\"\nexport * from \"./exchangepicker.vue?vue&type=script&lang=js\"\n\n\n/* normalize component */\nimport normalizer from \"!../../../../node_modules/vue-loader/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n null,\n null\n \n)\n\nexport default component.exports","\n\n\n\n\n","import mod from \"-!../../../node_modules/cache-loader/dist/cjs.js??ref--12-0!../../../node_modules/thread-loader/dist/cjs.js!../../../node_modules/babel-loader/lib/index.js!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./apiConfigBuilder.vue?vue&type=script&lang=js\"; export default mod; export * from \"-!../../../node_modules/cache-loader/dist/cjs.js??ref--12-0!../../../node_modules/thread-loader/dist/cjs.js!../../../node_modules/babel-loader/lib/index.js!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./apiConfigBuilder.vue?vue&type=script&lang=js\"","import { render, staticRenderFns } from \"./apiConfigBuilder.vue?vue&type=template&id=4a31b09e&lang=pug\"\nimport script from \"./apiConfigBuilder.vue?vue&type=script&lang=js\"\nexport * from \"./apiConfigBuilder.vue?vue&type=script&lang=js\"\nimport style0 from \"./apiConfigBuilder.vue?vue&type=style&index=0&lang=css\"\n\n\n/* normalize component */\nimport normalizer from \"!../../../node_modules/vue-loader/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n null,\n null\n \n)\n\nexport default component.exports","\n\n","import mod from \"-!../../../node_modules/cache-loader/dist/cjs.js??ref--12-0!../../../node_modules/thread-loader/dist/cjs.js!../../../node_modules/babel-loader/lib/index.js!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./config.vue?vue&type=script&lang=js\"; export default mod; export * from \"-!../../../node_modules/cache-loader/dist/cjs.js??ref--12-0!../../../node_modules/thread-loader/dist/cjs.js!../../../node_modules/babel-loader/lib/index.js!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./config.vue?vue&type=script&lang=js\"","import { render, staticRenderFns } from \"./config.vue?vue&type=template&id=d21dbde6&lang=pug\"\nimport script from \"./config.vue?vue&type=script&lang=js\"\nexport * from \"./config.vue?vue&type=script&lang=js\"\n\n\n/* normalize component */\nimport normalizer from \"!../../../node_modules/vue-loader/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n null,\n null\n \n)\n\nexport default component.exports","import mod from \"-!../../../node_modules/mini-css-extract-plugin/dist/loader.js!../../../node_modules/css-loader/index.js??ref--6-oneOf-1-1!../../../node_modules/vue-loader/lib/loaders/stylePostLoader.js!../../../node_modules/postcss-loader/lib/index.js??ref--6-oneOf-1-2!../../../node_modules/cache-loader/dist/cjs.js??ref--0-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./gekkoConfigBuilder.vue?vue&type=style&index=0&lang=css\"; export default mod; export * from \"-!../../../node_modules/mini-css-extract-plugin/dist/loader.js!../../../node_modules/css-loader/index.js??ref--6-oneOf-1-1!../../../node_modules/vue-loader/lib/loaders/stylePostLoader.js!../../../node_modules/postcss-loader/lib/index.js??ref--6-oneOf-1-2!../../../node_modules/cache-loader/dist/cjs.js??ref--0-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./gekkoConfigBuilder.vue?vue&type=style&index=0&lang=css\"","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',[_c('h3',[_vm._v(\"Daterange\")]),(_vm.tab === \"scan\")?[(!_vm.scanned)?_c('div',{staticClass:\"txt--center\"},[_c('a',{staticClass:\"w100--s btn--primary scan-btn\",attrs:{\"href\":\"#\"},on:{\"click\":function($event){$event.preventDefault();return _vm.scan($event)}}},[_vm._v(\"Scan available data\")])]):_vm._e(),(_vm.scanned == \"fetching\")?_c('div',{staticClass:\"txt--center\"},[_c('p',{staticClass:\"scan-btn\"},[_vm._v(\"Scanning..\")])]):_vm._e(),(_vm.scanned == true)?[(_vm.ranges.length === 0)?[_c('p',[_c('strong',[_vm._v(\"Unable to find any local data, do you have local data available for\\\"\"+_vm._s(_vm.config.watch.exchange)+\":\"+_vm._s(_vm.config.watch.currency)+\"/\"+_vm._s(_vm.config.watch.asset)+\"\\\"?\")])])]:[_c('label',{staticClass:\"wrapper\",attrs:{\"for\":\"exchange\"}},[_vm._v(\"Run simulation over:\")]),_c('form',{staticClass:\"radio grd\"},_vm._l((_vm.ranges),function(range,i){return _c('div',{staticClass:\"grd-row m1\"},[_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.selectedRangeIndex),expression:\"selectedRangeIndex\"}],staticClass:\"grd-row-col-1-6\",attrs:{\"type\":\"radio\"},domProps:{\"value\":i,\"checked\":_vm._q(_vm.selectedRangeIndex,i)},on:{\"change\":function($event){_vm.selectedRangeIndex=i}}}),_c('label',{staticClass:\"grd-row-col-5-6\",attrs:{\"for\":i}},[_vm._v(_vm._s(_vm.printRange(range)))])])}))],_c('p',[_c('em',[_c('a',{attrs:{\"href\":\"#\"},on:{\"click\":function($event){$event.preventDefault();return _vm.scan($event)}}},[_vm._v(\"rescan\")])])])]:_vm._e(),_c('p',{staticClass:\"txt--center\"},[_c('em',[_c('a',{attrs:{\"href\":\"#\"},on:{\"click\":function($event){$event.preventDefault();_vm.tab = \"manual\"}}},[_vm._v(\"Or manually set a daterange\")])])])]:_vm._e(),(_vm.tab === \"manual\")?[_c('div',[_c('label',{attrs:{\"for\":\"from\"}},[_vm._v(\"From:\")]),_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.from),expression:\"from\"}],domProps:{\"value\":(_vm.from)},on:{\"input\":function($event){if($event.target.composing){ return; }_vm.from=$event.target.value}}})]),_c('div',[_c('label',{attrs:{\"for\":\"to\"}},[_vm._v(\"To:\")]),_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.to),expression:\"to\"}],domProps:{\"value\":(_vm.to)},on:{\"input\":function($event){if($event.target.composing){ return; }_vm.to=$event.target.value}}})]),_c('p',{staticClass:\"txt--center\"}),_c('em',[_c('a',{attrs:{\"href\":\"#\"},on:{\"click\":function($event){$event.preventDefault();_vm.tab = \"scan\"}}},[_vm._v(\"Or scan for a daterange\")])])]:_vm._e()],2)}\nvar staticRenderFns = []\n\nexport { render, staticRenderFns }","\n\n\n\n\n","import mod from \"-!../../../../node_modules/cache-loader/dist/cjs.js??ref--12-0!../../../../node_modules/thread-loader/dist/cjs.js!../../../../node_modules/babel-loader/lib/index.js!../../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./rangepicker.vue?vue&type=script&lang=js\"; export default mod; export * from \"-!../../../../node_modules/cache-loader/dist/cjs.js??ref--12-0!../../../../node_modules/thread-loader/dist/cjs.js!../../../../node_modules/babel-loader/lib/index.js!../../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./rangepicker.vue?vue&type=script&lang=js\"","import { render, staticRenderFns } from \"./rangepicker.vue?vue&type=template&id=5037047f&lang=pug\"\nimport script from \"./rangepicker.vue?vue&type=script&lang=js\"\nexport * from \"./rangepicker.vue?vue&type=script&lang=js\"\nimport style0 from \"./rangepicker.vue?vue&type=style&index=0&lang=css\"\n\n\n/* normalize component */\nimport normalizer from \"!../../../../node_modules/vue-loader/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n null,\n null\n \n)\n\nexport default component.exports","import mod from \"-!../node_modules/mini-css-extract-plugin/dist/loader.js!../node_modules/css-loader/index.js??ref--6-oneOf-1-1!../node_modules/vue-loader/lib/loaders/stylePostLoader.js!../node_modules/postcss-loader/lib/index.js??ref--6-oneOf-1-2!../node_modules/cache-loader/dist/cjs.js??ref--0-0!../node_modules/vue-loader/lib/index.js??vue-loader-options!./App.vue?vue&type=style&index=0&lang=css\"; export default mod; export * from \"-!../node_modules/mini-css-extract-plugin/dist/loader.js!../node_modules/css-loader/index.js??ref--6-oneOf-1-1!../node_modules/vue-loader/lib/loaders/stylePostLoader.js!../node_modules/postcss-loader/lib/index.js??ref--6-oneOf-1-2!../node_modules/cache-loader/dist/cjs.js??ref--0-0!../node_modules/vue-loader/lib/index.js??vue-loader-options!./App.vue?vue&type=style&index=0&lang=css\"","import mod from \"-!../../../node_modules/mini-css-extract-plugin/dist/loader.js!../../../node_modules/css-loader/index.js??ref--6-oneOf-1-1!../../../node_modules/vue-loader/lib/loaders/stylePostLoader.js!../../../node_modules/postcss-loader/lib/index.js??ref--6-oneOf-1-2!../../../node_modules/cache-loader/dist/cjs.js??ref--0-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./apiConfigBuilder.vue?vue&type=style&index=0&lang=css\"; export default mod; export * from \"-!../../../node_modules/mini-css-extract-plugin/dist/loader.js!../../../node_modules/css-loader/index.js??ref--6-oneOf-1-1!../../../node_modules/vue-loader/lib/loaders/stylePostLoader.js!../../../node_modules/postcss-loader/lib/index.js??ref--6-oneOf-1-2!../../../node_modules/cache-loader/dist/cjs.js??ref--0-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./apiConfigBuilder.vue?vue&type=style&index=0&lang=css\"","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',[_c('h2',{staticClass:\"contain\"},[_vm._v(\"Backtest\")]),_c('div',{staticClass:\"hr\"}),_c('config-builder',{on:{\"config\":_vm.check}}),(_vm.backtestable)?_c('div',[_c('div',{staticClass:\"txt--center\"},[(_vm.backtestState !== \"fetching\")?_c('a',{staticClass:\"w100--s my1 btn--primary\",attrs:{\"href\":\"#\"},on:{\"click\":function($event){$event.preventDefault();return _vm.run($event)}}},[_vm._v(\"Backtest\")]):_vm._e(),(_vm.backtestState === \"fetching\")?_c('div',{staticClass:\"scan-btn\"},[_c('p',[_vm._v(\"Running backtest..\")]),_c('spinner')],1):_vm._e()])]):_vm._e(),(_vm.backtestResult && _vm.backtestState === \"fetched\")?_c('result',{attrs:{\"result\":_vm.backtestResult}}):_vm._e()],1)}\nvar staticRenderFns = []\n\nexport { render, staticRenderFns }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',[_c('dataset-picker',{staticClass:\"my2 contain\",on:{\"dataset\":_vm.updateDataset}}),_c('div',{staticClass:\"hr\"}),_c('strat-picker',{staticClass:\"my2 contain\",on:{\"stratConfig\":_vm.updateStrat}}),_c('div',{staticClass:\"hr\"}),_c('paper-trader',{staticClass:\"contain\",on:{\"settings\":_vm.updatePaperTrader}}),_c('div',{staticClass:\"hr\"})],1)}\nvar staticRenderFns = []\n\nexport { render, staticRenderFns }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',[_c('h3',[_vm._v(\"Select a dataset\")]),(_vm.datasetScanstate === \"idle\")?_c('div',{staticClass:\"txt--center my2\"},[_c('a',{staticClass:\"w100--s btn--primary scan-btn\",attrs:{\"href\":\"#\"},on:{\"click\":function($event){$event.preventDefault();return _vm.scan($event)}}},[_vm._v(\"Scan available data\")])]):_vm._e(),(_vm.datasetScanstate === \"scanning\")?_c('div',{staticClass:\"txt--center my2\"},[_c('spinner')],1):_vm._e(),(_vm.datasetScanstate === \"scanned\")?_c('div',{staticClass:\"my2\"},[(_vm.datasets.length != 0)?_c('div',[_c('table',{staticClass:\"full\"},[_vm._m(0),_c('tbody',_vm._l((_vm.datasets),function(set,i){return _c('tr',[_c('td',{staticClass:\"radio\"},[_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.setIndex),expression:\"setIndex\"}],attrs:{\"type\":\"radio\",\"name\":\"dataset\",\"id\":set.id},domProps:{\"value\":i,\"checked\":_vm._q(_vm.setIndex,i)},on:{\"change\":function($event){_vm.setIndex=i}}})]),_c('td',[_c('label',{attrs:{\"for\":set.id}},[_vm._v(_vm._s(set.exchange))])]),_c('td',[_c('label',{attrs:{\"for\":set.id}},[_vm._v(_vm._s(set.currency))])]),_c('td',[_c('label',{attrs:{\"for\":set.id}},[_vm._v(_vm._s(set.asset))])]),_c('td',[_c('label',{attrs:{\"for\":set.id}},[_vm._v(_vm._s(_vm.fmt(set.from)))])]),_c('td',[_c('label',{attrs:{\"for\":set.id}},[_vm._v(_vm._s(_vm.fmt(set.to)))])]),_c('td',[_c('label',{attrs:{\"for\":set.id}},[_vm._v(_vm._s(_vm.humanizeDuration(set.to.diff(set.from))))])])])}))]),(!_vm.rangeVisible)?_c('a',{staticClass:\"btn--primary\",attrs:{\"href\":\"#\"},on:{\"click\":function($event){$event.preventDefault();return _vm.openRange($event)}}},[_vm._v(\"Adjust range\")]):_vm._e(),(_vm.rangeVisible)?[_c('div',[_c('label',{attrs:{\"for\":\"customFrom\"}},[_vm._v(\"From:\")]),_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.customFrom),expression:\"customFrom\"}],domProps:{\"value\":(_vm.customFrom)},on:{\"input\":function($event){if($event.target.composing){ return; }_vm.customFrom=$event.target.value}}})]),_c('div',[_c('label',{attrs:{\"for\":\"customTo\"}},[_vm._v(\"To:\")]),_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.customTo),expression:\"customTo\"}],domProps:{\"value\":(_vm.customTo)},on:{\"input\":function($event){if($event.target.composing){ return; }_vm.customTo=$event.target.value}}})])]:_vm._e()],2):_c('em',[_vm._v(\"No Data found \"),_c('a',{attrs:{\"href\":\"#/data/importer\"}},[_vm._v(\"Lets add some\")])])]):_vm._e()])}\nvar staticRenderFns = [function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('thead',[_c('tr',[_c('th'),_c('th',[_vm._v(\"exchange\")]),_c('th',[_vm._v(\"currency\")]),_c('th',[_vm._v(\"asset\")]),_c('th',[_vm._v(\"from\")]),_c('th',[_vm._v(\"to\")]),_c('th',[_vm._v(\"duration\")])])])}]\n\nexport { render, staticRenderFns }","\n\n\n","import mod from \"-!../../../../node_modules/cache-loader/dist/cjs.js??ref--12-0!../../../../node_modules/thread-loader/dist/cjs.js!../../../../node_modules/babel-loader/lib/index.js!../../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./datasetpicker.vue?vue&type=script&lang=js\"; export default mod; export * from \"-!../../../../node_modules/cache-loader/dist/cjs.js??ref--12-0!../../../../node_modules/thread-loader/dist/cjs.js!../../../../node_modules/babel-loader/lib/index.js!../../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./datasetpicker.vue?vue&type=script&lang=js\"","import { render, staticRenderFns } from \"./datasetpicker.vue?vue&type=template&id=689b8a00&lang=pug\"\nimport script from \"./datasetpicker.vue?vue&type=script&lang=js\"\nexport * from \"./datasetpicker.vue?vue&type=script&lang=js\"\nimport style0 from \"./datasetpicker.vue?vue&type=style&index=0&lang=css\"\n\n\n/* normalize component */\nimport normalizer from \"!../../../../node_modules/vue-loader/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n null,\n null\n \n)\n\nexport default component.exports","\n\n\n\n\n","import mod from \"-!../../../node_modules/cache-loader/dist/cjs.js??ref--12-0!../../../node_modules/thread-loader/dist/cjs.js!../../../node_modules/babel-loader/lib/index.js!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./backtestConfigBuilder.vue?vue&type=script&lang=js\"; export default mod; export * from \"-!../../../node_modules/cache-loader/dist/cjs.js??ref--12-0!../../../node_modules/thread-loader/dist/cjs.js!../../../node_modules/babel-loader/lib/index.js!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./backtestConfigBuilder.vue?vue&type=script&lang=js\"","import { render, staticRenderFns } from \"./backtestConfigBuilder.vue?vue&type=template&id=0b74d9a3&lang=pug\"\nimport script from \"./backtestConfigBuilder.vue?vue&type=script&lang=js\"\nexport * from \"./backtestConfigBuilder.vue?vue&type=script&lang=js\"\nimport style0 from \"./backtestConfigBuilder.vue?vue&type=style&index=0&lang=css\"\n\n\n/* normalize component */\nimport normalizer from \"!../../../node_modules/vue-loader/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n null,\n null\n \n)\n\nexport default component.exports","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',[_c('div',{staticClass:\"hr contain\"}),_vm._m(0),_c('result-summary',{attrs:{\"report\":_vm.result.performanceReport}}),_c('div',{staticClass:\"hr contain\"}),_c('chart',{attrs:{\"data\":_vm.candles,\"height\":\"500\"}}),_c('div',{staticClass:\"hr contain\"}),_c('roundtripTable',{attrs:{\"roundtrips\":_vm.result.roundtrips}})],1)}\nvar staticRenderFns = [function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"contain\"},[_c('h3',[_vm._v(\"Backtest result\")])])}]\n\nexport { render, staticRenderFns }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"contain\"},[_c('div',{staticClass:\"grd-row summary\"},[_c('div',{staticClass:\"grd-row-col-3-6\"},[_c('table',{staticClass:\"p1\"},[_c('tr',[_c('th',[_vm._v(\"start time\")]),_c('td',[_vm._v(_vm._s(_vm.report.startTime))])]),_c('tr',[_c('th',[_vm._v(\"end time\")]),_c('td',[_vm._v(_vm._s(_vm.report.endTime))])]),_c('tr',[_c('th',[_vm._v(\"timespan\")]),_c('td',[_vm._v(_vm._s(_vm.report.timespan))])]),_c('tr',[_c('th',[_vm._v(\"start price\")]),_c('td',[_vm._v(_vm._s(_vm.round(_vm.report.startPrice))+\" \"+_vm._s(_vm.report.currency))])]),_c('tr',[_c('th',[_vm._v(\"end price\")]),_c('td',[_vm._v(_vm._s(_vm.round(_vm.report.endPrice))+\" \"+_vm._s(_vm.report.currency))])]),_c('tr',[_c('th',[_vm._v(\"market\")]),_c('td',[_vm._v(_vm._s(_vm.round(_vm.report.market))+\"%\")])])])]),_c('paperTradeSummary',{attrs:{\"report\":_vm.report}})],1)])}\nvar staticRenderFns = []\n\nexport { render, staticRenderFns }","\n\n\n\n\n","import mod from \"-!../../../../node_modules/cache-loader/dist/cjs.js??ref--12-0!../../../../node_modules/thread-loader/dist/cjs.js!../../../../node_modules/babel-loader/lib/index.js!../../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./summary.vue?vue&type=script&lang=js\"; export default mod; export * from \"-!../../../../node_modules/cache-loader/dist/cjs.js??ref--12-0!../../../../node_modules/thread-loader/dist/cjs.js!../../../../node_modules/babel-loader/lib/index.js!../../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./summary.vue?vue&type=script&lang=js\"","import { render, staticRenderFns } from \"./summary.vue?vue&type=template&id=5bfc891d&lang=pug\"\nimport script from \"./summary.vue?vue&type=script&lang=js\"\nexport * from \"./summary.vue?vue&type=script&lang=js\"\nimport style0 from \"./summary.vue?vue&type=style&index=0&lang=css\"\n\n\n/* normalize component */\nimport normalizer from \"!../../../../node_modules/vue-loader/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n null,\n null\n \n)\n\nexport default component.exports","\n\n\n\n\n","import mod from \"-!../../../../node_modules/cache-loader/dist/cjs.js??ref--12-0!../../../../node_modules/thread-loader/dist/cjs.js!../../../../node_modules/babel-loader/lib/index.js!../../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./result.vue?vue&type=script&lang=js\"; export default mod; export * from \"-!../../../../node_modules/cache-loader/dist/cjs.js??ref--12-0!../../../../node_modules/thread-loader/dist/cjs.js!../../../../node_modules/babel-loader/lib/index.js!../../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./result.vue?vue&type=script&lang=js\"","import { render, staticRenderFns } from \"./result.vue?vue&type=template&id=72b63acc&lang=pug\"\nimport script from \"./result.vue?vue&type=script&lang=js\"\nexport * from \"./result.vue?vue&type=script&lang=js\"\nimport style0 from \"./result.vue?vue&type=style&index=0&lang=css\"\n\n\n/* normalize component */\nimport normalizer from \"!../../../../node_modules/vue-loader/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n null,\n null\n \n)\n\nexport default component.exports","\n\n\n","import mod from \"-!../../../node_modules/cache-loader/dist/cjs.js??ref--12-0!../../../node_modules/thread-loader/dist/cjs.js!../../../node_modules/babel-loader/lib/index.js!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./backtester.vue?vue&type=script&lang=js\"; export default mod; export * from \"-!../../../node_modules/cache-loader/dist/cjs.js??ref--12-0!../../../node_modules/thread-loader/dist/cjs.js!../../../node_modules/babel-loader/lib/index.js!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./backtester.vue?vue&type=script&lang=js\"","import { render, staticRenderFns } from \"./backtester.vue?vue&type=template&id=da4202e8&lang=pug\"\nimport script from \"./backtester.vue?vue&type=script&lang=js\"\nexport * from \"./backtester.vue?vue&type=script&lang=js\"\n\n\n/* normalize component */\nimport normalizer from \"!../../../node_modules/vue-loader/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n null,\n null\n \n)\n\nexport default component.exports","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"contain\"},[_c('div',{staticClass:\"text\",domProps:{\"innerHTML\":_vm._s(_vm.intro)}}),_c('div',{staticClass:\"hr\"}),_c('h2',[_vm._v(\"Available datasets\")]),(_vm.datasetScanstate === \"idle\")?_c('div',{staticClass:\"txt--center my2\"},[_c('a',{staticClass:\"w100--s btn--primary scan-btn\",attrs:{\"href\":\"#\"},on:{\"click\":function($event){$event.preventDefault();return _vm.scan($event)}}},[_vm._v(\"Scan available data\")])]):_vm._e(),(_vm.datasetScanstate === \"scanning\")?_c('div',{staticClass:\"txt--center my2\"},[_c('spinner')],1):_vm._e(),(_vm.datasetScanstate === \"scanned\")?_c('div',{staticClass:\"my2\"},[(_vm.unscannableMakets.length)?_c('div',{staticClass:\"bg--orange p1 warning my1\"},[(!_vm.viewUnscannable)?_c('p',{staticClass:\"clickable\",on:{\"click\":function($event){$event.preventDefault();return _vm.toggleUnscannable($event)}}},[_vm._v(\"Some markets were unscannable, click here for details.\")]):_vm._e(),(_vm.viewUnscannable)?[_c('p',[_vm._v(\"Unable to find datasets in the following markets:\")]),_vm._l((_vm.unscannableMakets),function(market){return _c('div',{staticClass:\"mx2\"},[_vm._v(\"- \"+_vm._s(market.exchange)+\":\"+_vm._s(market.currency)+\":\"+_vm._s(market.asset))])})]:_vm._e()],2):_vm._e(),(_vm.datasets.length)?[_c('table',{staticClass:\"full data\"},[_vm._m(0),_c('tbody',_vm._l((_vm.datasets),function(set){return _c('tr',[_c('td',[_vm._v(_vm._s(set.exchange))]),_c('td',[_vm._v(_vm._s(set.currency))]),_c('td',[_vm._v(_vm._s(set.asset))]),_c('td',[_vm._v(_vm._s(_vm.fmt(set.from)))]),_c('td',[_vm._v(_vm._s(_vm.fmt(set.to)))]),_c('td',[_vm._v(_vm._s(_vm.humanizeDuration(set.to.diff(set.from))))])])}))])]:_vm._e(),(!_vm.datasets.length)?[_c('p',[_vm._v(\"It looks like you don't have any local data yet.\")])]:_vm._e()],2):_vm._e(),_c('div',{staticClass:\"my2\"},[_c('h2',[_vm._v(\"Import more data\")]),_c('p',{staticClass:\"text\"},[_vm._v(\"You can easily import more market data directly from exchanges using the importer.\")]),_c('router-link',{staticClass:\"btn--primary\",attrs:{\"to\":\"/data/importer\"}},[_vm._v(\"Go to the importer!\")])],1)])}\nvar staticRenderFns = [function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('thead',[_c('tr',[_c('th',[_vm._v(\"exchange\")]),_c('th',[_vm._v(\"currency\")]),_c('th',[_vm._v(\"asset\")]),_c('th',[_vm._v(\"from\")]),_c('th',[_vm._v(\"to\")]),_c('th',[_vm._v(\"duration\")])])])}]\n\nexport { render, staticRenderFns }","\n\n\n\n\n","import mod from \"-!../../../node_modules/cache-loader/dist/cjs.js??ref--12-0!../../../node_modules/thread-loader/dist/cjs.js!../../../node_modules/babel-loader/lib/index.js!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./data.vue?vue&type=script&lang=js\"; export default mod; export * from \"-!../../../node_modules/cache-loader/dist/cjs.js??ref--12-0!../../../node_modules/thread-loader/dist/cjs.js!../../../node_modules/babel-loader/lib/index.js!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./data.vue?vue&type=script&lang=js\"","import { render, staticRenderFns } from \"./data.vue?vue&type=template&id=6009d44c&lang=pug\"\nimport script from \"./data.vue?vue&type=script&lang=js\"\nexport * from \"./data.vue?vue&type=script&lang=js\"\nimport style0 from \"./data.vue?vue&type=style&index=0&lang=css\"\n\n\n/* normalize component */\nimport normalizer from \"!../../../node_modules/vue-loader/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n null,\n null\n \n)\n\nexport default component.exports","import mod from \"-!../../../../node_modules/mini-css-extract-plugin/dist/loader.js!../../../../node_modules/css-loader/index.js??ref--6-oneOf-1-1!../../../../node_modules/vue-loader/lib/loaders/stylePostLoader.js!../../../../node_modules/postcss-loader/lib/index.js??ref--6-oneOf-1-2!../../../../node_modules/cache-loader/dist/cjs.js??ref--0-0!../../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./roundtripTable.vue?vue&type=style&index=0&lang=css\"; export default mod; export * from \"-!../../../../node_modules/mini-css-extract-plugin/dist/loader.js!../../../../node_modules/css-loader/index.js??ref--6-oneOf-1-1!../../../../node_modules/vue-loader/lib/loaders/stylePostLoader.js!../../../../node_modules/postcss-loader/lib/index.js??ref--6-oneOf-1-2!../../../../node_modules/cache-loader/dist/cjs.js??ref--0-0!../../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./roundtripTable.vue?vue&type=style&index=0&lang=css\"","import mod from \"-!../../../node_modules/mini-css-extract-plugin/dist/loader.js!../../../node_modules/css-loader/index.js??ref--6-oneOf-1-1!../../../node_modules/vue-loader/lib/loaders/stylePostLoader.js!../../../node_modules/postcss-loader/lib/index.js??ref--6-oneOf-1-2!../../../node_modules/cache-loader/dist/cjs.js??ref--0-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./list.vue?vue&type=style&index=0&lang=css\"; export default mod; export * from \"-!../../../node_modules/mini-css-extract-plugin/dist/loader.js!../../../node_modules/css-loader/index.js??ref--6-oneOf-1-1!../../../node_modules/vue-loader/lib/loaders/stylePostLoader.js!../../../node_modules/postcss-loader/lib/index.js??ref--6-oneOf-1-2!../../../node_modules/cache-loader/dist/cjs.js??ref--0-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./list.vue?vue&type=style&index=0&lang=css\"","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"contain my2\"},[(_vm.data && !_vm.data.done)?_c('div',[_c('h2',[_vm._v(\"Importing data..\")]),_c('div',{staticClass:\"grd\"},[_c('div',{staticClass:\"grd-row\"},[_vm._m(0),_c('div',{staticClass:\"grd-row-col-4-6\"},[_vm._v(_vm._s(_vm.data.watch.exchange))])]),_c('div',{staticClass:\"grd-row\"},[_vm._m(1),_c('div',{staticClass:\"grd-row-col-4-6\"},[_vm._v(_vm._s(_vm.data.watch.currency)+\"/\"+_vm._s(_vm.data.watch.asset))])])]),_c('div',{staticClass:\"grd\"},[_c('div',{staticClass:\"grd-row\"},[_vm._m(2),_c('div',{staticClass:\"grd-row-col-4-6\"},[_vm._v(_vm._s(_vm.fmt(_vm.from)))])]),_c('div',{staticClass:\"grd-row\"},[_vm._m(3),_c('div',{staticClass:\"grd-row-col-4-6\"},[_vm._v(_vm._s(_vm.fmt(_vm.to)))])]),(_vm.initialized)?_c('div',{staticClass:\"grd-row\"},[_vm._m(4),_c('div',{staticClass:\"grd-row-col-4-6\"},[_vm._v(_vm._s(_vm.fmt(_vm.latest)))])]):_vm._e(),(_vm.initialized)?_c('div',{staticClass:\"grd-row\"},[_vm._m(5),_c('div',{staticClass:\"grd-row-col-4-6\"},[_vm._v(_vm._s(_vm.fromEnd))])]):_vm._e()]),(!_vm.initialized)?_c('spinner'):_vm._e(),(_vm.initialized)?_c('div',{staticClass:\"contain\"},[_c('progressBar',{attrs:{\"progress\":_vm.progress}})],1):_vm._e(),_c('p',[_c('em',[_vm._v(\"(you don't have to wait until the import is done,you can already start \"),_c('router-link',{attrs:{\"to\":\"/backtest\"}},[_vm._v(\"backtesting\")]),_vm._v(\").\")],1)])],1):_vm._e(),(_vm.data && _vm.data.done)?_c('div',{staticClass:\"txt--center\"},[_c('h2',[_vm._v(\"Import done\")]),_c('p',[_vm._v(\" Go and \"),_c('router-link',{attrs:{\"to\":\"/backtest\"}},[_vm._v(\"backtest\")]),_vm._v(\" with your new data!\")],1)]):_vm._e(),(!_vm.data)?_c('div',{staticClass:\"txt--center\"},[_c('h2',[_vm._v(\"ERROR: Unknown import\")]),_c('p',[_c('I',[_vm._v(\"don't know this import..\")])],1)]):_vm._e()])}\nvar staticRenderFns = [function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"grd-row-col-2-6\"},[_c('strong',[_vm._v(\"Market:\")])])},function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"grd-row-col-2-6\"},[_c('strong',[_vm._v(\"Currency/Asset:\")])])},function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"grd-row-col-2-6\"},[_c('strong',[_vm._v(\"From:\")])])},function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"grd-row-col-2-6\"},[_c('strong',[_vm._v(\"To:\")])])},function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"grd-row-col-2-6\"},[_c('strong',[_vm._v(\"Imported data until:\")])])},function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"grd-row-col-2-6\"},[_c('strong',[_vm._v(\"Remaining:\")])])}]\n\nexport { render, staticRenderFns }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return (_vm.progress)?_c('div',{staticClass:\"progressbarWrapper\"},[_c('p',[_c('strong',[_vm._v(_vm._s(_vm.round(_vm.progress))+\"%\")])]),_c('div',{staticClass:\"progressbar\"},[_c('div',{style:({width: _vm.progress + \"%\"})})])]):_vm._e()}\nvar staticRenderFns = []\n\nexport { render, staticRenderFns }","// http://stackoverflow.com/questions/7190898/progress-bar-with-html-and-css\n\n\n\n\n\n","import mod from \"-!../../../node_modules/cache-loader/dist/cjs.js??ref--12-0!../../../node_modules/thread-loader/dist/cjs.js!../../../node_modules/babel-loader/lib/index.js!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./progressBar.vue?vue&type=script&lang=js\"; export default mod; export * from \"-!../../../node_modules/cache-loader/dist/cjs.js??ref--12-0!../../../node_modules/thread-loader/dist/cjs.js!../../../node_modules/babel-loader/lib/index.js!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./progressBar.vue?vue&type=script&lang=js\"","import { render, staticRenderFns } from \"./progressBar.vue?vue&type=template&id=2b42ff94&lang=pug\"\nimport script from \"./progressBar.vue?vue&type=script&lang=js\"\nexport * from \"./progressBar.vue?vue&type=script&lang=js\"\nimport style0 from \"./progressBar.vue?vue&type=style&index=0&lang=css\"\n\n\n/* normalize component */\nimport normalizer from \"!../../../node_modules/vue-loader/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n null,\n null\n \n)\n\nexport default component.exports","\n\n\n\n\n","import mod from \"-!../../../../node_modules/cache-loader/dist/cjs.js??ref--12-0!../../../../node_modules/thread-loader/dist/cjs.js!../../../../node_modules/babel-loader/lib/index.js!../../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./single.vue?vue&type=script&lang=js\"; export default mod; export * from \"-!../../../../node_modules/cache-loader/dist/cjs.js??ref--12-0!../../../../node_modules/thread-loader/dist/cjs.js!../../../../node_modules/babel-loader/lib/index.js!../../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./single.vue?vue&type=script&lang=js\"","import { render, staticRenderFns } from \"./single.vue?vue&type=template&id=b930fa5e&lang=pug\"\nimport script from \"./single.vue?vue&type=script&lang=js\"\nexport * from \"./single.vue?vue&type=script&lang=js\"\nimport style0 from \"./single.vue?vue&type=style&index=0&lang=css\"\n\n\n/* normalize component */\nimport normalizer from \"!../../../../node_modules/vue-loader/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n null,\n null\n \n)\n\nexport default component.exports","import { get } from '../../../tools/ajax'\nimport store from '../../'\nimport { bus } from '../../../components/global/ws'\n\nconst init = () => {\n get('imports', (err, resp) => {\n store.commit('syncImports', resp);\n });\n}\n\nconst sync = () => {\n bus.$on('import_update', data => {\n store.commit('updateImport', data);\n });\n}\n\nexport default function() {\n init();\n sync();\n}","import { get } from '../../../tools/ajax'\nimport store from '../../'\nimport { bus } from '../../../components/global/ws'\nimport _ from 'lodash'\n\nconst init = () => {\n get('gekkos', (err, resp) => {\n const gekkos = resp;\n store.commit('syncGekkos', gekkos);\n });\n}\n\nconst sync = () => {\n bus.$on('gekko_new', data => store.commit('addGekko', data.state));\n bus.$on('gekko_event', data => store.commit('updateGekko', data));\n bus.$on('gekko_archived', data => store.commit('archiveGekko', data.id));\n bus.$on('gekko_error', data => store.commit('errorGekko', data));\n bus.$on('gekko_deleted', data => store.commit('deleteGekko', data.id));\n\n // unused:\n // bus.$on('gekko_stopped', data => store.commit('x', data.id));\n // bus.$on('gekko_deleted', data => store.commit('x', data.id));\n}\n\nexport default function() {\n init();\n sync();\n}","import store from '../../'\nimport { bus } from '../../../components/global/ws'\n\nconst init = () => {}\n\nconst sync = () => {\n bus.$on('WS_STATUS_CHANGE', ws => {\n return store.commit('setGlobalWarning', {key: 'connected', value: ws.connected});\n });\n}\n\nexport default function() {\n init();\n sync();\n}","import { get } from '../../../tools/ajax'\nimport store from '../../'\nimport { bus } from '../../../components/global/ws'\n\nconst transformMarkets = backendData => {\n if(!backendData) {\n return {};\n }\n\n var exchangesRaw = backendData;\n var exchangesTemp = {};\n\n exchangesRaw.forEach(e => {\n exchangesTemp[e.slug] = exchangesTemp[e.slug] || {markets: {}};\n\n e.markets.forEach( pair => {\n let [ currency, asset ] = pair['pair'];\n exchangesTemp[e.slug].markets[currency] = exchangesTemp[e.slug].markets[currency] || [];\n exchangesTemp[e.slug].markets[currency].push( asset );\n });\n\n if (\"exchangeMaxHistoryAge\" in e) {\n exchangesTemp[e.slug].exchangeMaxHistoryAge = e.exchangeMaxHistoryAge;\n }\n\n exchangesTemp[e.slug].importable = e.providesFullHistory ? true : false;\n exchangesTemp[e.slug].tradable = e.tradable ? true : false;\n exchangesTemp[e.slug].requires = e.requires;\n });\n\n return exchangesTemp;\n}\n\n\nconst init = () => {\n get('apiKeys', (err, resp) => {\n store.commit('syncApiKeys', resp);\n });\n\n get('exchanges', (err, resp) => {\n store.commit('syncExchanges', transformMarkets(resp));\n })\n}\n\nconst sync = () => {\n bus.$on('apiKeys', data => {\n store.commit('syncApiKeys', data.exchanges);\n });\n}\n\nexport default function() {\n init();\n sync();\n}\n","import Vue from 'vue'\nimport Vuex from 'vuex'\n\nimport syncImports from './modules/imports/sync'\nimport syncGekkos from './modules/gekkos/sync'\nimport syncNotifications from './modules/notifications/sync'\nimport syncConfig from './modules/config/sync'\n\nexport default function() {\n syncImports();\n syncGekkos();\n syncNotifications();\n syncConfig();\n}","import _ from 'lodash'\nimport Vue from 'vue'\n\nimport { wsPath } from '../../tools/api'\nimport initializeState from '../../store/init'\n\nvar socket = null;\n\nexport const bus = new Vue();\n\nbus.$on('gekko_update', data => console.log(data))\n\nbus.$on('import_update', data => console.log(data))\nbus.$on('import_error', data => {\n alert('IMPORT ERROR: ' + data.error);\n});\n\nconst info = {\n connected: false\n}\n\nexport const connect = () => {\n socket = new ReconnectingWebSocket(wsPath, null, { maxReconnectInterval: 4000 });\n\n setTimeout(() => {\n // in case we cannot connect\n if(!info.connected) {\n initializeState();\n bus.$emit('WS_STATUS_CHANGE', info);\n }\n }, 500);\n\n socket.onopen = () => {\n if(info.connected)\n return;\n\n info.connected = true;\n bus.$emit('WS_STATUS_CHANGE', info);\n initializeState();\n }\n socket.onclose = () => {\n if(!info.connected)\n return;\n\n info.connected = false;\n bus.$emit('WS_STATUS_CHANGE', info);\n }\n socket.onerror = () => {\n if(!info.connected)\n return;\n\n info.connected = false;\n bus.$emit('WS_STATUS_CHANGE', info);\n }\n socket.onmessage = function(message) {\n const payload = JSON.parse(message.data);\n // console.log('ws message:', payload);\n bus.$emit(payload.type, payload);\n };\n}","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"contain py2\"},[_c('h3',[_vm._v(\"Market watchers\")]),(!_vm.watchers.length)?_c('div',{staticClass:\"text\"},[_c('p',[_vm._v(\"You don't have any market watchers.\")])]):_vm._e(),(_vm.watchers.length)?_c('table',{staticClass:\"full clickable\"},[_vm._m(0),_c('tbody',_vm._l((_vm.watchers),function(gekko){return _c('tr',{staticClass:\"clickable\",on:{\"click\":function($event){_vm.$router.push({path: (\"/live-gekkos/\" + (gekko.id))})}}},[_c('td',[_vm._v(_vm._s(gekko.config.watch.exchange))]),_c('td',[_vm._v(_vm._s(gekko.config.watch.currency))]),_c('td',[_vm._v(_vm._s(gekko.config.watch.asset))]),_c('td',[_vm._v(_vm._s(_vm.status(gekko)))]),_c('td',[(gekko.events.initial.candle)?[_vm._v(_vm._s(_vm.fmt(gekko.events.initial.candle.start)))]:_vm._e()],2),_c('td',[(gekko.events.latest.candle)?[_vm._v(_vm._s(_vm.fmt(gekko.events.latest.candle.start)))]:_vm._e()],2),_c('td',[(gekko.events.initial.candle && gekko.events.latest.candle)?[_vm._v(_vm._s(_vm.timespan(gekko.events.latest.candle.start, gekko.events.initial.candle.start)))]:_vm._e()],2)])}))]):_vm._e(),_c('h3',[_vm._v(\"Strat runners\")]),(!_vm.stratrunners.length)?_c('div',{staticClass:\"text\"},[_c('p',[_vm._v(\"You don't have any stratrunners.\")])]):_vm._e(),(_vm.stratrunners.length)?_c('table',{staticClass:\"full\"},[_vm._m(1),_c('tbody',_vm._l((_vm.stratrunners),function(gekko){return _c('tr',{staticClass:\"clickable\",on:{\"click\":function($event){_vm.$router.push({path: (\"/live-gekkos/\" + (gekko.id))})}}},[_c('td',[_vm._v(_vm._s(gekko.config.watch.exchange))]),_c('td',[_vm._v(_vm._s(gekko.config.watch.currency))]),_c('td',[_vm._v(_vm._s(gekko.config.watch.asset))]),_c('td',[_vm._v(_vm._s(_vm.status(gekko)))]),_c('td',[(gekko.events.initial.candle && gekko.events.latest.candle)?[_vm._v(_vm._s(_vm.timespan(gekko.events.latest.candle.start, gekko.events.initial.candle.start)))]:_vm._e()],2),_c('td',[_vm._v(_vm._s(gekko.config.tradingAdvisor.method))]),_c('td',[(!_vm.report(gekko))?[_vm._v(\"0\")]:_vm._e(),(_vm.report(gekko))?[_vm._v(_vm._s(_vm.round(_vm.report(gekko).profit))+\" \"+_vm._s(_vm.report(gekko).currency))]:_vm._e()],2),_c('td',[_vm._v(_vm._s(gekko.logType))]),_c('td',[(!gekko.events.tradeCompleted)?[_vm._v(\"0\")]:_vm._e(),(gekko.events.tradeCompleted)?[_vm._v(_vm._s(gekko.events.tradeCompleted.length))]:_vm._e()],2)])}))]):_vm._e(),_c('div',{staticClass:\"hr\"}),_c('h2',[_vm._v(\"Start a new live Gekko\")]),_c('router-link',{staticClass:\"btn--primary\",attrs:{\"to\":\"/live-gekkos/new\"}},[_vm._v(\"Start a new live Gekko!\")])],1)}\nvar staticRenderFns = [function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('thead',[_c('tr',[_c('th',[_vm._v(\"exchange\")]),_c('th',[_vm._v(\"currency\")]),_c('th',[_vm._v(\"asset\")]),_c('th',[_vm._v(\"status\")]),_c('th',[_vm._v(\"started at\")]),_c('th',[_vm._v(\"last update\")]),_c('th',[_vm._v(\"duration\")])])])},function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('thead',[_c('tr',[_c('th',[_vm._v(\"exchange\")]),_c('th',[_vm._v(\"currency\")]),_c('th',[_vm._v(\"asset\")]),_c('th',[_vm._v(\"status\")]),_c('th',[_vm._v(\"duration\")]),_c('th',[_vm._v(\"strategy\")]),_c('th',[_vm._v(\"PnL\")]),_c('th',[_vm._v(\"type\")]),_c('th',[_vm._v(\"trades\")])])])}]\n\nexport { render, staticRenderFns }","\n\n\n\n\n","import mod from \"-!../../../node_modules/cache-loader/dist/cjs.js??ref--12-0!../../../node_modules/thread-loader/dist/cjs.js!../../../node_modules/babel-loader/lib/index.js!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./list.vue?vue&type=script&lang=js\"; export default mod; export * from \"-!../../../node_modules/cache-loader/dist/cjs.js??ref--12-0!../../../node_modules/thread-loader/dist/cjs.js!../../../node_modules/babel-loader/lib/index.js!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./list.vue?vue&type=script&lang=js\"","import { render, staticRenderFns } from \"./list.vue?vue&type=template&id=55dd2514&lang=pug\"\nimport script from \"./list.vue?vue&type=script&lang=js\"\nexport * from \"./list.vue?vue&type=script&lang=js\"\nimport style0 from \"./list.vue?vue&type=style&index=0&lang=css\"\n\n\n/* normalize component */\nimport normalizer from \"!../../../node_modules/vue-loader/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n null,\n null\n \n)\n\nexport default component.exports","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"contain my2\"},[_c('div',{staticClass:\"text\",domProps:{\"innerHTML\":_vm._s(_vm.intro)}}),_c('div',{staticClass:\"hr\"}),_c('h3',[_vm._v(\"Currently running imports\")]),(_vm.imports.length === 0)?_c('p',[_vm._v(\"You currently don't have any imports running.\")]):_vm._e(),(_vm.imports.length)?_c('ul',_vm._l((_vm.imports),function(_import){return _c('li',[_c('router-link',{attrs:{\"to\":\"/data/importer/import/\" + _import.id}},[_vm._v(_vm._s(_import.watch.exchange)+\":\"+_vm._s(_import.watch.currency)+\"/\"+_vm._s(_import.watch.asset))])],1)})):_vm._e(),_c('div',{staticClass:\"hr\"}),_c('h3',[_vm._v(\"Start a new import\")]),_c('import-config-builder',{on:{\"config\":_vm.updateConfig}}),_c('div',{staticClass:\"hr\"}),_c('div',{staticClass:\"txt--center\"},[_c('a',{staticClass:\"w100--s my1 btn--primary\",attrs:{\"href\":\"#\"},on:{\"click\":function($event){$event.preventDefault();return _vm.run($event)}}},[_vm._v(\"Import\")])])],1)}\nvar staticRenderFns = []\n\nexport { render, staticRenderFns }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"grd contain\"},[_c('div',{staticClass:\"grd-row\"},[_c('div',{staticClass:\"grd-row-col-3-6 mx1\"},[_c('h3',[_vm._v(\"Market\")]),_c('market-picker',{attrs:{\"only-importable\":\"true\"},on:{\"market\":_vm.updateMarketConfig}})],1),_c('div',{staticClass:\"grd-row-col-3-6 mx1\"},[_c('range-creator',{on:{\"range\":_vm.updateRange}})],1)])])}\nvar staticRenderFns = []\n\nexport { render, staticRenderFns }","\n\n\n\n\n","import mod from \"-!../../../../node_modules/cache-loader/dist/cjs.js??ref--12-0!../../../../node_modules/thread-loader/dist/cjs.js!../../../../node_modules/babel-loader/lib/index.js!../../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./importConfigBuilder.vue?vue&type=script&lang=js\"; export default mod; export * from \"-!../../../../node_modules/cache-loader/dist/cjs.js??ref--12-0!../../../../node_modules/thread-loader/dist/cjs.js!../../../../node_modules/babel-loader/lib/index.js!../../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./importConfigBuilder.vue?vue&type=script&lang=js\"","import { render, staticRenderFns } from \"./importConfigBuilder.vue?vue&type=template&id=a8c578c6&lang=pug\"\nimport script from \"./importConfigBuilder.vue?vue&type=script&lang=js\"\nexport * from \"./importConfigBuilder.vue?vue&type=script&lang=js\"\nimport style0 from \"./importConfigBuilder.vue?vue&type=style&index=0&lang=css\"\n\n\n/* normalize component */\nimport normalizer from \"!../../../../node_modules/vue-loader/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n null,\n null\n \n)\n\nexport default component.exports","\n\n\n\n\n","import mod from \"-!../../../../node_modules/cache-loader/dist/cjs.js??ref--12-0!../../../../node_modules/thread-loader/dist/cjs.js!../../../../node_modules/babel-loader/lib/index.js!../../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./importer.vue?vue&type=script&lang=js\"; export default mod; export * from \"-!../../../../node_modules/cache-loader/dist/cjs.js??ref--12-0!../../../../node_modules/thread-loader/dist/cjs.js!../../../../node_modules/babel-loader/lib/index.js!../../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./importer.vue?vue&type=script&lang=js\"","import { render, staticRenderFns } from \"./importer.vue?vue&type=template&id=3e819299&lang=pug\"\nimport script from \"./importer.vue?vue&type=script&lang=js\"\nexport * from \"./importer.vue?vue&type=script&lang=js\"\nimport style0 from \"./importer.vue?vue&type=style&index=0&lang=css\"\n\n\n/* normalize component */\nimport normalizer from \"!../../../../node_modules/vue-loader/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n null,\n null\n \n)\n\nexport default component.exports","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('section',{staticClass:\"contain grd-row\"},[_c('div',{staticClass:\"grd-row-col-3-6\",domProps:{\"innerHTML\":_vm._s(_vm.left)}}),_vm._m(0)])}\nvar staticRenderFns = [function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"grd-row-col-3-6 txt--center\"},[_c('img',{attrs:{\"src\":\"static/gekko.jpg\"}}),_c('p',[_c('em',[_vm._v(\"The most valuable commodity I know of is information.\")])])])}]\n\nexport { render, staticRenderFns }","\n\n\n","import mod from \"-!../../../node_modules/cache-loader/dist/cjs.js??ref--12-0!../../../node_modules/thread-loader/dist/cjs.js!../../../node_modules/babel-loader/lib/index.js!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./home.vue?vue&type=script&lang=js\"; export default mod; export * from \"-!../../../node_modules/cache-loader/dist/cjs.js??ref--12-0!../../../node_modules/thread-loader/dist/cjs.js!../../../node_modules/babel-loader/lib/index.js!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./home.vue?vue&type=script&lang=js\"","import { render, staticRenderFns } from \"./home.vue?vue&type=template&id=2758f780&lang=pug\"\nimport script from \"./home.vue?vue&type=script&lang=js\"\nexport * from \"./home.vue?vue&type=script&lang=js\"\n\n\n/* normalize component */\nimport normalizer from \"!../../../node_modules/vue-loader/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n null,\n null\n \n)\n\nexport default component.exports","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"my2\"},[(!_vm.data)?_c('div',{staticClass:\"contain\"},[_c('h1',[_vm._v(\"Unknown Gekko instance\")]),_c('p',[_vm._v(\"Gekko doesn't know what gekko this is...\")])]):_vm._e(),(_vm.data)?_c('div',[_c('h2',{staticClass:\"contain\"},[_vm._v(\"Gekko \"+_vm._s(_vm.type))]),(_vm.isArchived)?_c('div',{staticClass:\"contain brdr--mid-gray p1 bg--orange\"},[_vm._v(\"This is an archived Gekko, it is currently not running anymore.\")]):_vm._e(),(_vm.data.errorMessage)?_c('div',{staticClass:\"contain brdr--mid-gray p1 bg--orange\"},[_vm._v(\"This is Gekko crashed with the following error: \"+_vm._s(_vm.data.errorMessage))]):_vm._e(),_c('div',{staticClass:\"grd contain\"},[_c('div',{staticClass:\"grd-row\"},[_c('div',{staticClass:\"grd-row-col-3-6\"},[_c('h3',[_vm._v(\"Market\")]),_c('div',{staticClass:\"grd-row\"},[_c('div',{staticClass:\"grd-row-col-3-6\"},[_vm._v(\"Exchange\")]),_c('div',{staticClass:\"grd-row-col-3-6\"},[_vm._v(_vm._s(_vm.config.watch.exchange))])]),_c('div',{staticClass:\"grd-row\"},[_c('div',{staticClass:\"grd-row-col-3-6\"},[_vm._v(\"Currency\")]),_c('div',{staticClass:\"grd-row-col-3-6\"},[_vm._v(_vm._s(_vm.config.watch.currency))])]),_c('div',{staticClass:\"grd-row\"},[_c('div',{staticClass:\"grd-row-col-3-6\"},[_vm._v(\"Asset\")]),_c('div',{staticClass:\"grd-row-col-3-6\"},[_vm._v(_vm._s(_vm.config.watch.asset))])]),_c('div',{staticClass:\"grd-row\"},[_c('div',{staticClass:\"grd-row-col-3-6\"},[_vm._v(\"Type\")]),_c('div',{staticClass:\"grd-row-col-3-6\"},[_vm._v(_vm._s(_vm.type))])])]),_c('div',{staticClass:\"grd-row-col-3-6\"},[_c('h3',[_vm._v(\"Runtime\")]),(_vm.isLoading)?_c('spinner'):_vm._e(),(!_vm.isLoading)?[(_vm.initialEvents.candle)?_c('div',{staticClass:\"grd-row\"},[_c('div',{staticClass:\"grd-row-col-2-6\"},[_vm._v(\"Watching since\")]),_c('div',{staticClass:\"grd-row-col-4-6\"},[_vm._v(_vm._s(_vm.fmt(_vm.initialEvents.candle.start)))])]):_vm._e(),(_vm.latestEvents.candle)?_c('div',{staticClass:\"grd-row\"},[_c('div',{staticClass:\"grd-row-col-2-6\"},[_vm._v(\"Received data until\")]),_c('div',{staticClass:\"grd-row-col-4-6\"},[_vm._v(_vm._s(_vm.fmt(_vm.latestEvents.candle.start)))])]):_vm._e(),(_vm.latestEvents.candle)?_c('div',{staticClass:\"grd-row\"},[_c('div',{staticClass:\"grd-row-col-2-6\"},[_vm._v(\"Data spanning\")]),_c('div',{staticClass:\"grd-row-col-4-6\"},[_vm._v(_vm._s(_vm.humanizeDuration(_vm.moment(_vm.latestEvents.candle.start).diff(_vm.moment(_vm.initialEvents.candle.start)))))])]):_vm._e(),(_vm.isStratrunner)?[_c('div',{staticClass:\"grd-row\"},[_c('div',{staticClass:\"grd-row-col-2-6\"},[_vm._v(\"Amount of trades\")]),_c('div',{staticClass:\"grd-row-col-4-6\"},[_vm._v(_vm._s(_vm.trades.length))])]),_c('div',{staticClass:\"grd-row\"},[_c('div',{staticClass:\"grd-row-col-2-6\"},[_vm._v(\"Candle size\")]),_c('div',{staticClass:\"grd-row-col-4-6\"},[_vm._v(_vm._s(_vm.config.tradingAdvisor.candleSize))])]),_c('div',{staticClass:\"grd-row\"},[_c('div',{staticClass:\"grd-row-col-2-6\"},[_vm._v(\"History size\")]),_c('div',{staticClass:\"grd-row-col-4-6\"},[_vm._v(_vm._s(_vm.config.tradingAdvisor.historySize))])])]:_vm._e()]:_vm._e()],2)]),(_vm.warmupRemaining)?_c('div',{staticClass:\"contain brdr--mid-gray p1 bg--orange\"},[_vm._v(\"This stratrunner is still warming up for the next \"),_c('i',[_vm._v(_vm._s(_vm.warmupRemaining.replace(',', ' and ')))]),_vm._v(\", it will not trade until it is warmed up.\")]):_vm._e(),(_vm.isStratrunner)?_c('div',{staticClass:\"grd-row\"},[_c('div',{staticClass:\"grd-row-col-3-6\"},[_c('h3',[_vm._v(\"Strategy\")]),_c('div',{staticClass:\"grd-row\"},[_c('div',{staticClass:\"grd-row-col-3-6\"},[_vm._v(\"Name\")]),_c('div',{staticClass:\"grd-row-col-3-6\"},[_c('strong',[_vm._v(_vm._s(_vm.stratName))])])]),_vm._v(\"Parameters\"),_c('pre',[_vm._v(_vm._s(_vm.stratParams))])]),_c('div',{staticClass:\"grd-row-col-3-6\"},[_c('h3',[_vm._v(\"Profit report\")]),(!_vm.report)?[_c('p',[(_vm.isArchived)?_c('em',[_vm._v(\"This Gekko never executed a trade..\")]):_vm._e(),(!_vm.isArchived)?_c('em',[_vm._v(\"Waiting for at least one trade..\")]):_vm._e()])]:_vm._e(),(_vm.report)?[_c('div',{staticClass:\"grd-row\"},[_c('div',{staticClass:\"grd-row-col-3-6\"},[_vm._v(\"Start balance\")]),_c('div',{staticClass:\"grd-row-col-3-6\"},[_vm._v(_vm._s(_vm.round(_vm.report.startBalance)))])]),_c('div',{staticClass:\"grd-row\"},[_c('div',{staticClass:\"grd-row-col-3-6\"},[_vm._v(\"Current balance\")]),_c('div',{staticClass:\"grd-row-col-3-6\"},[_vm._v(_vm._s(_vm.round(_vm.report.balance)))])]),_c('div',{staticClass:\"grd-row\"},[_c('div',{staticClass:\"grd-row-col-3-6\"},[_vm._v(\"Market\")]),_c('div',{staticClass:\"grd-row-col-3-6\"},[_vm._v(_vm._s(_vm.round(_vm.report.market / 100 * _vm.report.startPrice))+\" \"+_vm._s(_vm.config.watch.currency)+\" (\"+_vm._s(_vm.round(_vm.report.market))+\" %)\")])]),_c('div',{staticClass:\"grd-row\"},[_c('div',{staticClass:\"grd-row-col-3-6\"},[_vm._v(\"Profit\")]),_c('div',{staticClass:\"grd-row-col-3-6\"},[_vm._v(_vm._s(_vm.round(_vm.report.profit))+\" \"+_vm._s(_vm.config.watch.currency)+\" (\"+_vm._s(_vm.round(_vm.report.relativeProfit))+\" %)\")])]),_c('div',{staticClass:\"grd-row\"},[_c('div',{staticClass:\"grd-row-col-3-6\"},[_vm._v(\"Alpha\")]),_c('div',{staticClass:\"grd-row-col-3-6\"},[_vm._v(_vm._s(_vm.round(_vm.report.alpha))+\" \"+_vm._s(_vm.config.watch.currency))])])]:_vm._e()],2)]):_vm._e(),(_vm.isStratrunner && !_vm.watcher && !_vm.isArchived)?_c('p',[_vm._v(\"WARNING: stale gekko, not attached to a watcher, please report \"),_c('a',{attrs:{\"href\":\"https://github.com/askmike/gekko/issues\"}},[_vm._v(\"here\")]),_vm._v(\".\")]):_vm._e(),(!_vm.isArchived)?_c('p',[_c('a',{staticClass:\"w100--s my1 btn--red\",on:{\"click\":_vm.stopGekko}},[_vm._v(\"Stop Gekko\")])]):_vm._e(),(_vm.isArchived)?_c('p',[_c('a',{staticClass:\"w100--s my1 btn--red\",on:{\"click\":_vm.deleteGekko}},[_vm._v(\"Delete Gekko\")])]):_vm._e(),(_vm.isStratrunner && _vm.watcher && !_vm.isArchived)?_c('p',[_c('em',[_vm._v(\"This gekko gets market data from \"),_c('router-link',{attrs:{\"to\":\"/live-gekkos/\" + _vm.watcher.id}},[_vm._v(\"this market watcher\")])],1),_vm._v(\".\")]):_vm._e()]),(!_vm.isLoading)?[_c('h3',{staticClass:\"contain\"},[_vm._v(\"Market graph\")]),(_vm.candleFetch === \"fetching\")?_c('spinner'):_vm._e(),(_vm.candleFetch === \"fetched\")?[_c('chart',{attrs:{\"data\":_vm.chartData,\"height\":300}})]:_vm._e(),(_vm.isStratrunner)?_c('roundtrips',{attrs:{\"roundtrips\":_vm.roundtrips}}):_vm._e()]:_vm._e()],2):_vm._e()])}\nvar staticRenderFns = []\n\nexport { render, staticRenderFns }","\n\n\n\n\n","import mod from \"-!../../../node_modules/cache-loader/dist/cjs.js??ref--12-0!../../../node_modules/thread-loader/dist/cjs.js!../../../node_modules/babel-loader/lib/index.js!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./singleGekko.vue?vue&type=script&lang=js\"; export default mod; export * from \"-!../../../node_modules/cache-loader/dist/cjs.js??ref--12-0!../../../node_modules/thread-loader/dist/cjs.js!../../../node_modules/babel-loader/lib/index.js!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./singleGekko.vue?vue&type=script&lang=js\"","import { render, staticRenderFns } from \"./singleGekko.vue?vue&type=template&id=5ce0f611&lang=pug\"\nimport script from \"./singleGekko.vue?vue&type=script&lang=js\"\nexport * from \"./singleGekko.vue?vue&type=script&lang=js\"\nimport style0 from \"./singleGekko.vue?vue&type=style&index=0&lang=css\"\n\n\n/* normalize component */\nimport normalizer from \"!../../../node_modules/vue-loader/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n null,\n null\n \n)\n\nexport default component.exports","import mod from \"-!../../../../node_modules/mini-css-extract-plugin/dist/loader.js!../../../../node_modules/css-loader/index.js??ref--6-oneOf-1-1!../../../../node_modules/vue-loader/lib/loaders/stylePostLoader.js!../../../../node_modules/postcss-loader/lib/index.js??ref--6-oneOf-1-2!../../../../node_modules/cache-loader/dist/cjs.js??ref--0-0!../../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./datasetpicker.vue?vue&type=style&index=0&lang=css\"; export default mod; export * from \"-!../../../../node_modules/mini-css-extract-plugin/dist/loader.js!../../../../node_modules/css-loader/index.js??ref--6-oneOf-1-1!../../../../node_modules/vue-loader/lib/loaders/stylePostLoader.js!../../../../node_modules/postcss-loader/lib/index.js??ref--6-oneOf-1-2!../../../../node_modules/cache-loader/dist/cjs.js??ref--0-0!../../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./datasetpicker.vue?vue&type=style&index=0&lang=css\"","import mod from \"-!../../../node_modules/mini-css-extract-plugin/dist/loader.js!../../../node_modules/css-loader/index.js??ref--6-oneOf-1-1!../../../node_modules/vue-loader/lib/loaders/stylePostLoader.js!../../../node_modules/postcss-loader/lib/index.js??ref--6-oneOf-1-2!../../../node_modules/cache-loader/dist/cjs.js??ref--0-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./blockSpinner.vue?vue&type=style&index=0&lang=css\"; export default mod; export * from \"-!../../../node_modules/mini-css-extract-plugin/dist/loader.js!../../../node_modules/css-loader/index.js??ref--6-oneOf-1-1!../../../node_modules/vue-loader/lib/loaders/stylePostLoader.js!../../../node_modules/postcss-loader/lib/index.js??ref--6-oneOf-1-2!../../../node_modules/cache-loader/dist/cjs.js??ref--0-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./blockSpinner.vue?vue&type=style&index=0&lang=css\"","import mod from \"-!../../../../node_modules/mini-css-extract-plugin/dist/loader.js!../../../../node_modules/css-loader/index.js??ref--6-oneOf-1-1!../../../../node_modules/vue-loader/lib/loaders/stylePostLoader.js!../../../../node_modules/postcss-loader/lib/index.js??ref--6-oneOf-1-2!../../../../node_modules/cache-loader/dist/cjs.js??ref--0-0!../../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./single.vue?vue&type=style&index=0&lang=css\"; export default mod; export * from \"-!../../../../node_modules/mini-css-extract-plugin/dist/loader.js!../../../../node_modules/css-loader/index.js??ref--6-oneOf-1-1!../../../../node_modules/vue-loader/lib/loaders/stylePostLoader.js!../../../../node_modules/postcss-loader/lib/index.js??ref--6-oneOf-1-2!../../../../node_modules/cache-loader/dist/cjs.js??ref--0-0!../../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./single.vue?vue&type=style&index=0&lang=css\"","// global window.CONFIG\n\nconst config = window.CONFIG.ui;\nconst endpoint = `${config.host}${config.port === 80 ? '' : `:${config.port}`}${config.path}`;\n\nlet basePath, restPath, wsPath;\n\n// rest API path\nif(config.ssl) {\n basePath = `https://${endpoint}`;\n} else {\n basePath = `http://${endpoint}`;\n}\n\nrestPath = basePath + 'api/';\n\n// ws API path\nif(config.ssl) {\n wsPath = `wss://${endpoint}api`;\n} else {\n wsPath = `ws://${endpoint}api`;\n}\n\nexport {\n wsPath,\n restPath,\n basePath\n};\n","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"grd\"},[_c('div',{staticClass:\"px1\"},[_c('h3',[_vm._v(\"Paper trader\")]),(_vm.toggle === \"closed\")?_c('a',{staticClass:\"btn--primary\",attrs:{\"href\":\"#\"},on:{\"click\":function($event){$event.preventDefault();return _vm.switchToggle($event)}}},[_vm._v(\"Change paper trader settings\")]):_vm._e(),(_vm.toggle === \"open\")?[_c('p',[_vm._v(\"Settings:\")]),_c('textarea',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.rawPaperTraderParams),expression:\"rawPaperTraderParams\"}],staticClass:\"params\",domProps:{\"value\":(_vm.rawPaperTraderParams)},on:{\"input\":function($event){if($event.target.composing){ return; }_vm.rawPaperTraderParams=$event.target.value}}}),(_vm.rawPaperTraderParamsError)?_c('p',{staticClass:\"bg--red p1\"},[_vm._v(_vm._s(_vm.rawPaperTraderParamsError.message))]):_vm._e()]:_vm._e()],2)])}\nvar staticRenderFns = []\n\nexport { render, staticRenderFns }","\n\n\n\n","import mod from \"-!../../../../node_modules/cache-loader/dist/cjs.js??ref--12-0!../../../../node_modules/thread-loader/dist/cjs.js!../../../../node_modules/babel-loader/lib/index.js!../../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./papertrader.vue?vue&type=script&lang=js\"; export default mod; export * from \"-!../../../../node_modules/cache-loader/dist/cjs.js??ref--12-0!../../../../node_modules/thread-loader/dist/cjs.js!../../../../node_modules/babel-loader/lib/index.js!../../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./papertrader.vue?vue&type=script&lang=js\"","import { render, staticRenderFns } from \"./papertrader.vue?vue&type=template&id=6a733e04&lang=pug\"\nimport script from \"./papertrader.vue?vue&type=script&lang=js\"\nexport * from \"./papertrader.vue?vue&type=script&lang=js\"\nimport style0 from \"./papertrader.vue?vue&type=style&index=0&lang=css\"\n\n\n/* normalize component */\nimport normalizer from \"!../../../../node_modules/vue-loader/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n null,\n null\n \n)\n\nexport default component.exports","import { post } from '../../../tools/ajax'\n\nvar mixin = {\n data: () => {\n return {\n datasets: [],\n datasetScanstate: 'idle',\n unscannableMakets: []\n } \n },\n methods: {\n scan: function() {\n this.datasetScanstate = 'scanning';\n\n post('scansets', {}, (error, response) => {\n this.datasetScanstate = 'scanned';\n\n this.unscannableMakets = response.errors;\n\n let sets = [];\n\n response.datasets.forEach(market => {\n market.ranges.forEach((range, i) => {\n sets.push({\n exchange: market.exchange,\n currency: market.currency,\n asset: market.asset,\n from: moment.unix(range.from).utc(),\n to: moment.unix(range.to).utc(),\n id: market.exchange + market.asset + market.currency + i\n });\n });\n });\n\n // for now, filter out sets smaller than 3 hours..\n sets = sets.filter(set => {\n if(set.to.diff(set.from, 'hours') > 2)\n return true;\n });\n\n sets = sets.sort((a, b) => {\n let adiff = a.to.diff(a.from);\n let bdiff = b.to.diff(b.from);\n\n if(adiff < bdiff)\n return -1;\n\n if(adiff > bdiff)\n return 1;\n\n return 0;\n }).reverse();\n\n this.datasets = sets;\n })\n }\n }\n}\n\nexport default mixin;","import mod from \"-!../../../../node_modules/mini-css-extract-plugin/dist/loader.js!../../../../node_modules/css-loader/index.js??ref--6-oneOf-1-1!../../../../node_modules/vue-loader/lib/loaders/stylePostLoader.js!../../../../node_modules/postcss-loader/lib/index.js??ref--6-oneOf-1-2!../../../../node_modules/cache-loader/dist/cjs.js??ref--0-0!../../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./stratpicker.vue?vue&type=style&index=0&lang=css\"; export default mod; export * from \"-!../../../../node_modules/mini-css-extract-plugin/dist/loader.js!../../../../node_modules/css-loader/index.js??ref--6-oneOf-1-1!../../../../node_modules/vue-loader/lib/loaders/stylePostLoader.js!../../../../node_modules/postcss-loader/lib/index.js??ref--6-oneOf-1-2!../../../../node_modules/cache-loader/dist/cjs.js??ref--0-0!../../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./stratpicker.vue?vue&type=style&index=0&lang=css\"","import mod from \"-!../../../node_modules/mini-css-extract-plugin/dist/loader.js!../../../node_modules/css-loader/index.js??ref--6-oneOf-1-1!../../../node_modules/vue-loader/lib/loaders/stylePostLoader.js!../../../node_modules/postcss-loader/lib/index.js??ref--6-oneOf-1-2!../../../node_modules/cache-loader/dist/cjs.js??ref--0-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./paperTradeSummary.vue?vue&type=style&index=0&lang=css\"; export default mod; export * from \"-!../../../node_modules/mini-css-extract-plugin/dist/loader.js!../../../node_modules/css-loader/index.js??ref--6-oneOf-1-1!../../../node_modules/vue-loader/lib/loaders/stylePostLoader.js!../../../node_modules/postcss-loader/lib/index.js??ref--6-oneOf-1-2!../../../node_modules/cache-loader/dist/cjs.js??ref--0-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./paperTradeSummary.vue?vue&type=style&index=0&lang=css\"","import mod from \"-!../../../node_modules/mini-css-extract-plugin/dist/loader.js!../../../node_modules/css-loader/index.js??ref--6-oneOf-1-1!../../../node_modules/vue-loader/lib/loaders/stylePostLoader.js!../../../node_modules/postcss-loader/lib/index.js??ref--6-oneOf-1-2!../../../node_modules/cache-loader/dist/cjs.js??ref--0-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./header.vue?vue&type=style&index=0&lang=css\"; export default mod; export * from \"-!../../../node_modules/mini-css-extract-plugin/dist/loader.js!../../../node_modules/css-loader/index.js??ref--6-oneOf-1-1!../../../node_modules/vue-loader/lib/loaders/stylePostLoader.js!../../../node_modules/postcss-loader/lib/index.js??ref--6-oneOf-1-2!../../../node_modules/cache-loader/dist/cjs.js??ref--0-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./header.vue?vue&type=style&index=0&lang=css\"","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"contain roundtrips\"},[_c('h2',[_vm._v(\"Roundtrips\")]),(_vm.roundtrips.length)?_c('table',[_c('thead',[_vm._m(0),_vm._l((_vm.roundtrips),function(rt){return _c('tr',[_c('td',[_vm._v(_vm._s(_vm.fmt(rt.entryAt)))]),_c('td',[_vm._v(_vm._s(_vm.fmt(rt.exitAt)))]),_c('td',[_vm._v(_vm._s(_vm.diff(rt.duration)))]),_c('td',[_vm._v(_vm._s(_vm.round(rt.entryBalance)))]),_c('td',[_vm._v(_vm._s(_vm.round(rt.exitBalance)))]),(Math.sign(rt.pnl)===-1)?[_c('td',{staticClass:\"loss\"},[_vm._v(_vm._s(Math.sign(rt.pnl)*rt.pnl.toFixed(2)))]),_c('td',{staticClass:\"loss\"},[_vm._v(_vm._s(rt.profit.toFixed(2))+\"%\")])]:[_c('td',{staticClass:\"profit\"},[_vm._v(_vm._s(rt.pnl.toFixed(2)))]),_c('td',{staticClass:\"profit\"},[_vm._v(_vm._s(rt.profit.toFixed(2))+\"%\")])]],2)})],2)]):_vm._e(),(!_vm.roundtrips.length)?_c('div',[_c('p',[_vm._v(\"Not enough data to display\")])]):_vm._e()])}\nvar staticRenderFns = [function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('tr',[_c('th',[_vm._v(\"Entry at (UTC)\")]),_c('th',[_vm._v(\"Exit at (UTC)\")]),_c('th',[_vm._v(\"Exposure\")]),_c('th',[_vm._v(\"Entry balance\")]),_c('th',[_vm._v(\"Exit balance\")]),_c('th',[_vm._v(\"P&L\")]),_c('th',[_vm._v(\"Profit\")])])}]\n\nexport { render, staticRenderFns }","\n\n\n\n\n","import mod from \"-!../../../../node_modules/cache-loader/dist/cjs.js??ref--12-0!../../../../node_modules/thread-loader/dist/cjs.js!../../../../node_modules/babel-loader/lib/index.js!../../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./roundtripTable.vue?vue&type=script&lang=js\"; export default mod; export * from \"-!../../../../node_modules/cache-loader/dist/cjs.js??ref--12-0!../../../../node_modules/thread-loader/dist/cjs.js!../../../../node_modules/babel-loader/lib/index.js!../../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./roundtripTable.vue?vue&type=script&lang=js\"","import { render, staticRenderFns } from \"./roundtripTable.vue?vue&type=template&id=3c32021a&lang=pug\"\nimport script from \"./roundtripTable.vue?vue&type=script&lang=js\"\nexport * from \"./roundtripTable.vue?vue&type=script&lang=js\"\nimport style0 from \"./roundtripTable.vue?vue&type=style&index=0&lang=css\"\n\n\n/* normalize component */\nimport normalizer from \"!../../../../node_modules/vue-loader/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n null,\n null\n \n)\n\nexport default component.exports","import mod from \"-!../../../../node_modules/mini-css-extract-plugin/dist/loader.js!../../../../node_modules/css-loader/index.js??ref--6-oneOf-1-1!../../../../node_modules/vue-loader/lib/loaders/stylePostLoader.js!../../../../node_modules/postcss-loader/lib/index.js??ref--6-oneOf-1-2!../../../../node_modules/cache-loader/dist/cjs.js??ref--0-0!../../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./result.vue?vue&type=style&index=0&lang=css\"; export default mod; export * from \"-!../../../../node_modules/mini-css-extract-plugin/dist/loader.js!../../../../node_modules/css-loader/index.js??ref--6-oneOf-1-1!../../../../node_modules/vue-loader/lib/loaders/stylePostLoader.js!../../../../node_modules/postcss-loader/lib/index.js??ref--6-oneOf-1-2!../../../../node_modules/cache-loader/dist/cjs.js??ref--0-0!../../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./result.vue?vue&type=style&index=0&lang=css\"","import mod from \"-!../../../../node_modules/mini-css-extract-plugin/dist/loader.js!../../../../node_modules/css-loader/index.js??ref--6-oneOf-1-1!../../../../node_modules/vue-loader/lib/loaders/stylePostLoader.js!../../../../node_modules/postcss-loader/lib/index.js??ref--6-oneOf-1-2!../../../../node_modules/cache-loader/dist/cjs.js??ref--0-0!../../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./typepicker.vue?vue&type=style&index=0&lang=css\"; export default mod; export * from \"-!../../../../node_modules/mini-css-extract-plugin/dist/loader.js!../../../../node_modules/css-loader/index.js??ref--6-oneOf-1-1!../../../../node_modules/vue-loader/lib/loaders/stylePostLoader.js!../../../../node_modules/postcss-loader/lib/index.js??ref--6-oneOf-1-2!../../../../node_modules/cache-loader/dist/cjs.js??ref--0-0!../../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./typepicker.vue?vue&type=style&index=0&lang=css\"","import superagent from 'superagent'\nimport noCache from 'superagent-no-cache'\nimport { restPath } from './api.js'\n\nconst processResponse = next => (err, res) => {\n if(err)\n return next(err);\n\n if(!res.text)\n return next('no data');\n\n let data = JSON.parse(res.text);\n\n next(false, data);\n}\n\nexport const post = (to, data, next) => {\n superagent\n .post(restPath + to)\n .use(noCache)\n .send(data)\n .end(processResponse(next));\n}\n\nexport const get = (to, next) => {\n superagent\n .get(restPath + to)\n .use(noCache)\n .end(processResponse(next));\n}\n","import mod from \"-!../../../../node_modules/mini-css-extract-plugin/dist/loader.js!../../../../node_modules/css-loader/index.js??ref--6-oneOf-1-1!../../../../node_modules/vue-loader/lib/loaders/stylePostLoader.js!../../../../node_modules/postcss-loader/lib/index.js??ref--6-oneOf-1-2!../../../../node_modules/cache-loader/dist/cjs.js??ref--0-0!../../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./importer.vue?vue&type=style&index=0&lang=css\"; export default mod; export * from \"-!../../../../node_modules/mini-css-extract-plugin/dist/loader.js!../../../../node_modules/css-loader/index.js??ref--6-oneOf-1-1!../../../../node_modules/vue-loader/lib/loaders/stylePostLoader.js!../../../../node_modules/postcss-loader/lib/index.js??ref--6-oneOf-1-2!../../../../node_modules/cache-loader/dist/cjs.js??ref--0-0!../../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./importer.vue?vue&type=style&index=0&lang=css\""],"sourceRoot":""} \ No newline at end of file diff --git a/web/vue/dist/app.730569ff.css b/web/vue/dist/app.730569ff.css new file mode 100644 index 0000000..631b5b9 --- /dev/null +++ b/web/vue/dist/app.730569ff.css @@ -0,0 +1 @@ +.menu{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row;margin-top:0;margin-bottom:2rem}.menu a{-webkit-box-flex:1;-ms-flex:1 1 100%;flex:1 1 100%;display:block;text-align:center;text-decoration:none;color:inherit}.menu .router-link-active{background-color:hsla(0,0%,98%,.99)}.menu a:hover{text-decoration:underline}#modal-background{position:fixed;top:0;bottom:0;left:0;right:0;background-color:#000;opacity:.5}.modal{position:fixed;top:50%;left:50%;-webkit-transform:translate(-50%,-50%);transform:translate(-50%,-50%);width:600px;min-height:300px;background-color:#fff}.modal-guts{position:absolute;top:0;left:0;width:100%;height:100%;padding:20px 50px 20px 20px;overflow:auto}#app{display:-webkit-box;display:-ms-flexbox;display:flex;min-height:100vh;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column}.fill{-webkit-box-flex:1;-ms-flex:1;flex:1}.text{max-width:500px}input{background:none;margin-top:.5em}.params{min-height:235px;line-height:1.3em}.hr{margin-top:2rem;margin-bottom:2rem;height:10px;background-color:hsla(0,0%,98%,.99)}.contain{width:900px;margin-left:auto;margin-right:auto}.btn--primary{display:inline-block;margin-right:12px;margin-bottom:12px;height:40px;padding:0 18px;border-radius:4px;text-shadow:0 1px 3px rgba(36,180,126,.4);-webkit-box-shadow:0 4px 6px rgba(50,50,93,.11),0 1px 3px rgba(0,0,0,.08);box-shadow:0 4px 6px rgba(50,50,93,.11),0 1px 3px rgba(0,0,0,.08);line-height:40px;-webkit-transition:-webkit-transform .25s;transition:-webkit-transform .25s;transition:transform .25s;transition:transform .25s,-webkit-transform .25s}.btn--primary,.btn--primary:hover{background-color:#3498db;color:#fff;text-decoration:none}.btn--primary:hover{-webkit-transform:translateY(-1px);transform:translateY(-1px);-webkit-box-shadow:0 7px 14px rgba(50,50,93,.1),0 3px 6px rgba(0,0,0,.08);box-shadow:0 7px 14px rgba(50,50,93,.1),0 3px 6px rgba(0,0,0,.08)}.btn--primary:active,.btn--primary:focus{background-color:#3498db;color:#fff;text-decoration:none}.btn--primary:active{-webkit-transform:translateY(1px);transform:translateY(1px)}.spinner{margin:20px auto 100px;width:50px;height:40px;text-align:center;font-size:10px}.spinner>div{background-color:#333;height:100%;width:6px;display:inline-block;margin-right:4px;-webkit-animation:sk-stretchdelay 1.2s infinite ease-in-out;animation:sk-stretchdelay 1.2s infinite ease-in-out}.spinner .rect2{-webkit-animation-delay:-1.1s;animation-delay:-1.1s}.spinner .rect3{-webkit-animation-delay:-1s;animation-delay:-1s}.spinner .rect4{-webkit-animation-delay:-.9s;animation-delay:-.9s}.spinner .rect5{-webkit-animation-delay:-.8s;animation-delay:-.8s}@-webkit-keyframes sk-stretchdelay{0%,40%,to{-webkit-transform:scaleY(.4)}20%{-webkit-transform:scaleY(1)}}@keyframes sk-stretchdelay{0%,40%,to{transform:scaleY(.4);-webkit-transform:scaleY(.4)}20%{transform:scaleY(1);-webkit-transform:scaleY(1)}}td.radio{width:45px}td label{display:inline;font-size:1em}.align .custom-select select{padding:.4em 1.2em .3em .8em}.label-like{display:block;font-size:.9em;color:#777}.align{padding-left:1em}.big{font-size:1.3em}.big,.summary table{width:80%}.price.profit{color:#7fff00}.price.loss{color:red}.summary td{text-align:right}#chartWrapper.clickable{position:relative}#chartWrapper.clickable .shield{cursor:-webkit-zoom-in;cursor:zoom-in;position:absolute;top:0;bottom:0;left:0;right:0;background:grey;opacity:.1}#chart{background-color:#eee;width:100%}#chart circle{-webkit-clip-path:url(#clip);clip-path:url(#clip)}#chart .zoom{cursor:move;fill:none;pointer-events:all}#chart .line{fill:none;stroke:#4682b4;stroke-width:1.5px;-webkit-clip-path:url(#clip);clip-path:url(#clip)}#chart circle.buy{fill:#7fff00}#chart circle.sell{fill:red}.roundtrips{margin-top:50px;margin-bottom:50px}.roundtrips table{width:100%}.roundtrips table td,.roundtrips table th{border:1px solid #c6cbd1;padding:8px 12px}.roundtrips table td.loss{color:red;text-align:right}.roundtrips table td.profit{color:green;text-align:right}.roundtrips table tr:nth-child(2n){background-color:#f6f8fa}.clickable{cursor:pointer}table.full{width:100%}table.full td{padding:.5rem 0}table.full.data th{text-align:left;padding:.5rem 0}.warning p{margin:0;padding:0}.scan-btn{margin-top:80px;margin-bottom:30px}.progressbarWrapper p{text-align:center;font-size:20px}.progressbar{background-color:hsla(0,0%,85%,.99);border-radius:13px;padding:0}@-webkit-keyframes shimmer{0%{background-position:0 0}to{background-position:960px 0}}@keyframes shimmer{0%{background-position:0 0}to{background-position:960px 0}}.progressbar>div{height:20px;border-radius:10px;background-color:orange;-webkit-animation-duration:1.5s;animation-duration:1.5s;-webkit-animation-fill-mode:forwards;animation-fill-mode:forwards;-webkit-animation-iteration-count:infinite;animation-iteration-count:infinite;-webkit-animation-name:shimmer;animation-name:shimmer;-webkit-animation-timing-function:linear;animation-timing-function:linear;background:#f6f7f8;background:-webkit-gradient(linear,left top,right top,color-stop(10%,orange),color-stop(25%,#ffce77),color-stop(40%,orange));background:linear-gradient(90deg,orange 10%,#ffce77 25%,orange 40%);background-size:960px 50px;background-position:0;position:relative}table.clickable{border-collapse:separate}tr.clickable td:first-child{padding-left:5px}tr.clickable{cursor:pointer}tr.clickable:hover{background:hsla(0,0%,85%,.99)}.radio label{margin-top:0} \ No newline at end of file diff --git a/web/vue/dist/chunk-vendors.b9a11975.js b/web/vue/dist/chunk-vendors.b9a11975.js new file mode 100644 index 0000000..5805f8c --- /dev/null +++ b/web/vue/dist/chunk-vendors.b9a11975.js @@ -0,0 +1,26 @@ +(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([[0],{"+rLv":function(t,e,n){var r=n("dyZX").document;t.exports=r&&r.documentElement},"/yFf":function(t,e,n){"use strict";var r=n("oHnp");function i(t){if(t)return o(t)}function o(t){for(var e in i.prototype)t[e]=i.prototype[e];return t}t.exports=i,i.prototype.get=function(t){return this.header[t.toLowerCase()]},i.prototype._setHeaderProperties=function(t){var e=t["content-type"]||"";this.type=r.type(e);var n=r.params(e);for(var i in n)this[i]=n[i];this.links={};try{t.link&&(this.links=r.parseLinks(t.link))}catch(t){}},i.prototype._setStatusProperties=function(t){var e=t/100|0;this.status=this.statusCode=t,this.statusType=e,this.info=1==e,this.ok=2==e,this.redirect=3==e,this.clientError=4==e,this.serverError=5==e,this.error=(4==e||5==e)&&this.toError(),this.created=201==t,this.accepted=202==t,this.noContent=204==t,this.badRequest=400==t,this.unauthorized=401==t,this.notAcceptable=406==t,this.forbidden=403==t,this.notFound=404==t,this.unprocessableEntity=422==t}},"0/R4":function(t,e){t.exports=function(t){return"object"===typeof t?null!==t:"function"===typeof t}},"0sh+":function(t,e,n){var r=n("quPj"),i=n("vhPU");t.exports=function(t,e,n){if(r(e))throw TypeError("String#"+n+" doesn't accept regex!");return String(i(t))}},"1TsA":function(t,e){t.exports=function(t,e){return{value:e,done:!!t}}},"24Ii":function(t,e,n){var r;"undefined"!==typeof window?r=window:"undefined"!==typeof self?r=self:(console.warn("Using browser-only version of superagent in non-browser environment"),r=this);var i=n("cpc2"),o=n("kMlx"),a=n("8zgK"),u=n("/yFf"),s=n("nZbv");function c(){}var l=e=t.exports=function(t,n){return"function"==typeof n?new e.Request("GET",t).end(n):1==arguments.length?new e.Request("GET",t):new e.Request(t,n)};e.Request=_,l.getXHR=function(){if(!(!r.XMLHttpRequest||r.location&&"file:"==r.location.protocol&&r.ActiveXObject))return new XMLHttpRequest;try{return new ActiveXObject("Microsoft.XMLHTTP")}catch(t){}try{return new ActiveXObject("Msxml2.XMLHTTP.6.0")}catch(t){}try{return new ActiveXObject("Msxml2.XMLHTTP.3.0")}catch(t){}try{return new ActiveXObject("Msxml2.XMLHTTP")}catch(t){}throw Error("Browser-only version of superagent could not find XHR")};var f="".trim?function(t){return t.trim()}:function(t){return t.replace(/(^\s*|\s*$)/g,"")};function p(t){if(!a(t))return t;var e=[];for(var n in t)h(e,n,t[n]);return e.join("&")}function h(t,e,n){if(null!=n)if(Array.isArray(n))n.forEach(function(n){h(t,e,n)});else if(a(n))for(var r in n)h(t,e+"["+r+"]",n[r]);else t.push(encodeURIComponent(e)+"="+encodeURIComponent(n));else null===n&&t.push(encodeURIComponent(e))}function d(t){for(var e,n,r={},i=t.split("&"),o=0,a=i.length;o=2&&t._responseTimeoutTimer&&clearTimeout(t._responseTimeoutTimer),4==n){var r;try{r=e.status}catch(t){r=0}if(!r){if(t.timedout||t._aborted)return;return t.crossDomainError()}t.emit("end")}};var r=function(e,n){n.total>0&&(n.percent=n.loaded/n.total*100),n.direction=e,t.emit("progress",n)};if(this.hasListeners("progress"))try{e.onprogress=r.bind(null,"download"),e.upload&&(e.upload.onprogress=r.bind(null,"upload"))}catch(t){}try{this.username&&this.password?e.open(this.method,this.url,!0,this.username,this.password):e.open(this.method,this.url,!0)}catch(t){return this.callback(t)}if(this._withCredentials&&(e.withCredentials=!0),!this._formData&&"GET"!=this.method&&"HEAD"!=this.method&&"string"!=typeof n&&!this._isHost(n)){var i=this._header["content-type"],o=this._serializer||l.serialize[i?i.split(";")[0]:""];!o&&y(i)&&(o=l.serialize["application/json"]),o&&(n=o(n))}for(var a in this.header)null!=this.header[a]&&this.header.hasOwnProperty(a)&&e.setRequestHeader(a,this.header[a]);return this._responseType&&(e.responseType=this._responseType),this.emit("request",this),e.send("undefined"!==typeof n?n:null),this},l.agent=function(){return new s},["GET","POST","OPTIONS","PATCH","PUT","DELETE"].forEach(function(t){s.prototype[t.toLowerCase()]=function(e,n){var r=new l.Request(t,e);return this._setDefaults(r),n&&r.end(n),r}}),s.prototype.del=s.prototype["delete"],l.get=function(t,e,n){var r=l("GET",t);return"function"==typeof e&&(n=e,e=null),e&&r.query(e),n&&r.end(n),r},l.head=function(t,e,n){var r=l("HEAD",t);return"function"==typeof e&&(n=e,e=null),e&&r.query(e),n&&r.end(n),r},l.options=function(t,e,n){var r=l("OPTIONS",t);return"function"==typeof e&&(n=e,e=null),e&&r.send(e),n&&r.end(n),r},l["del"]=m,l["delete"]=m,l.patch=function(t,e,n){var r=l("PATCH",t);return"function"==typeof e&&(n=e,e=null),e&&r.send(e),n&&r.end(n),r},l.post=function(t,e,n){var r=l("POST",t);return"function"==typeof e&&(n=e,e=null),e&&r.send(e),n&&r.end(n),r},l.put=function(t,e,n){var r=l("PUT",t);return"function"==typeof e&&(n=e,e=null),e&&r.send(e),n&&r.end(n),r}},"2OiF":function(t,e){t.exports=function(t){if("function"!=typeof t)throw TypeError(t+" is not a function!");return t}},"3Lyj":function(t,e,n){var r=n("KroJ");t.exports=function(t,e,n){for(var i in e)r(t,i,e[i],n);return t}},"4R4u":function(t,e){t.exports="constructor,hasOwnProperty,isPrototypeOf,propertyIsEnumerable,toLocaleString,toString,valueOf".split(",")},"69bn":function(t,e,n){var r=n("y3w9"),i=n("2OiF"),o=n("K0xU")("species");t.exports=function(t,e){var n,a=r(t).constructor;return void 0===a||void 0==(n=r(a)[o])?e:i(n)}},"6FMO":function(t,e,n){var r=n("0/R4"),i=n("EWmC"),o=n("K0xU")("species");t.exports=function(t){var e;return i(t)&&(e=t.constructor,"function"!=typeof e||e!==Array&&!i(e.prototype)||(e=void 0),r(e)&&(e=e[o],null===e&&(e=void 0))),void 0===e?Array:e}},"8zgK":function(t,e,n){"use strict";function r(t){return null!==t&&"object"===typeof t}t.exports=r},"91GP":function(t,e,n){var r=n("XKFU");r(r.S+r.F,"Object",{assign:n("czNK")})},"9gX7":function(t,e){t.exports=function(t,e,n,r){if(!(t instanceof e)||void 0!==r&&r in t)throw TypeError(n+": incorrect invocation!");return t}},Afnz:function(t,e,n){"use strict";var r=n("LQAc"),i=n("XKFU"),o=n("KroJ"),a=n("Mukb"),u=n("hPIQ"),s=n("QaDb"),c=n("fyDq"),l=n("OP3Y"),f=n("K0xU")("iterator"),p=!([].keys&&"next"in[].keys()),h="@@iterator",d="keys",v="values",y=function(){return this};t.exports=function(t,e,n,g,_,m,b){s(n,e,g);var w,x,k,A=function(t){if(!p&&t in j)return j[t];switch(t){case d:return function(){return new n(this,t)};case v:return function(){return new n(this,t)}}return function(){return new n(this,t)}},O=e+" Iterator",C=_==v,E=!1,j=t.prototype,S=j[f]||j[h]||_&&j[_],$=S||A(_),T=_?C?A("entries"):$:void 0,R="Array"==e&&j.entries||S;if(R&&(k=l(R.call(new t)),k!==Object.prototype&&k.next&&(c(k,O,!0),r||"function"==typeof k[f]||a(k,f,y))),C&&S&&S.name!==v&&(E=!0,$=function(){return S.call(this)}),r&&!b||!p&&!E&&j[f]||a(j,f,$),u[e]=$,u[O]=y,_)if(w={values:C?$:A(v),keys:m?$:A(d),entries:T},b)for(x in w)x in j||o(j,x,w[x]);else i(i.P+i.F*(p||E),e,w);return w}},"C/va":function(t,e,n){"use strict";var r=n("y3w9");t.exports=function(){var t=r(this),e="";return t.global&&(e+="g"),t.ignoreCase&&(e+="i"),t.multiline&&(e+="m"),t.unicode&&(e+="u"),t.sticky&&(e+="y"),e}},CkkT:function(t,e,n){var r=n("m0Pp"),i=n("Ymqv"),o=n("S/j/"),a=n("ne8i"),u=n("zRwo");t.exports=function(t,e){var n=1==t,s=2==t,c=3==t,l=4==t,f=6==t,p=5==t||f,h=e||u;return function(e,u,d){for(var v,y,g=o(e),_=i(g),m=r(u,d,3),b=a(_.length),w=0,x=n?h(e,b):s?h(e,0):void 0;b>w;w++)if((p||w in _)&&(v=_[w],y=m(v,w,g),t))if(n)x[w]=y;else if(y)switch(t){case 3:return!0;case 5:return v;case 6:return w;case 2:x.push(v)}else if(l)return!1;return f?-1:c||l?l:x}}},DVgA:function(t,e,n){var r=n("zhAb"),i=n("4R4u");t.exports=Object.keys||function(t){return r(t,i)}},DlQD:function(t,e,n){(function(e){(function(e){"use strict";var n={newline:/^\n+/,code:/^( {4}[^\n]+\n*)+/,fences:v,hr:/^ {0,3}((?:- *){3,}|(?:_ *){3,}|(?:\* *){3,})(?:\n+|$)/,heading:/^ *(#{1,6}) *([^\n]+?) *(?:#+ *)?(?:\n+|$)/,nptable:v,blockquote:/^( {0,3}> ?(paragraph|[^\n]*)(?:\n|$))+/,list:/^( *)(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,html:"^ {0,3}(?:<(script|pre|style)[\\s>][\\s\\S]*?(?:[^\\n]*\\n+|$)|comment[^\\n]*(\\n+|$)|<\\?[\\s\\S]*?\\?>\\n*|\\n*|\\n*|)[\\s\\S]*?(?:\\n{2,}|$)|<(?!script|pre|style)([a-z][\\w-]*)(?:attribute)*? */?>(?=\\h*\\n)[\\s\\S]*?(?:\\n{2,}|$)|(?=\\h*\\n)[\\s\\S]*?(?:\\n{2,}|$))",def:/^ {0,3}\[(label)\]: *\n? *]+)>?(?:(?: +\n? *| *\n *)(title))? *(?:\n+|$)/,table:v,lheading:/^([^\n]+)\n *(=|-){2,} *(?:\n+|$)/,paragraph:/^([^\n]+(?:\n(?!hr|heading|lheading| {0,3}>|<\/?(?:tag)(?: +|\n|\/?>)|<(?:script|pre|style|!--))[^\n]+)*)/,text:/^[^\n]+/};function r(t){this.tokens=[],this.tokens.links={},this.options=t||_.defaults,this.rules=n.normal,this.options.pedantic?this.rules=n.pedantic:this.options.gfm&&(this.options.tables?this.rules=n.tables:this.rules=n.gfm)}n._label=/(?!\s*\])(?:\\[\[\]]|[^\[\]])+/,n._title=/(?:"(?:\\"?|[^"\\])*"|'[^'\n]*(?:\n[^'\n]+)*\n?'|\([^()]*\))/,n.def=f(n.def).replace("label",n._label).replace("title",n._title).getRegex(),n.bullet=/(?:[*+-]|\d+\.)/,n.item=/^( *)(bull) [^\n]*(?:\n(?!\1bull )[^\n]*)*/,n.item=f(n.item,"gm").replace(/bull/g,n.bullet).getRegex(),n.list=f(n.list).replace(/bull/g,n.bullet).replace("hr","\\n+(?=\\1?(?:(?:- *){3,}|(?:_ *){3,}|(?:\\* *){3,})(?:\\n+|$))").replace("def","\\n+(?="+n.def.source+")").getRegex(),n._tag="address|article|aside|base|basefont|blockquote|body|caption|center|col|colgroup|dd|details|dialog|dir|div|dl|dt|fieldset|figcaption|figure|footer|form|frame|frameset|h[1-6]|head|header|hr|html|iframe|legend|li|link|main|menu|menuitem|meta|nav|noframes|ol|optgroup|option|p|param|section|source|summary|table|tbody|td|tfoot|th|thead|title|tr|track|ul",n._comment=//,n.html=f(n.html,"i").replace("comment",n._comment).replace("tag",n._tag).replace("attribute",/ +[a-zA-Z:_][\w.:-]*(?: *= *"[^"\n]*"| *= *'[^'\n]*'| *= *[^\s"'=<>`]+)?/).getRegex(),n.paragraph=f(n.paragraph).replace("hr",n.hr).replace("heading",n.heading).replace("lheading",n.lheading).replace("tag",n._tag).getRegex(),n.blockquote=f(n.blockquote).replace("paragraph",n.paragraph).getRegex(),n.normal=y({},n),n.gfm=y({},n.normal,{fences:/^ *(`{3,}|~{3,})[ \.]*(\S+)? *\n([\s\S]*?)\n? *\1 *(?:\n+|$)/,paragraph:/^/,heading:/^ *(#{1,6}) +([^\n]+?) *#* *(?:\n+|$)/}),n.gfm.paragraph=f(n.paragraph).replace("(?!","(?!"+n.gfm.fences.source.replace("\\1","\\2")+"|"+n.list.source.replace("\\1","\\3")+"|").getRegex(),n.tables=y({},n.gfm,{nptable:/^ *([^|\n ].*\|.*)\n *([-:]+ *\|[-| :]*)(?:\n((?:.*[^>\n ].*(?:\n|$))*)\n*|$)/,table:/^ *\|(.+)\n *\|?( *[-:]+[-| :]*)(?:\n((?: *[^>\n ].*(?:\n|$))*)\n*|$)/}),n.pedantic=y({},n.normal,{html:f("^ *(?:comment *(?:\\n|\\s*$)|<(tag)[\\s\\S]+? *(?:\\n{2,}|\\s*$)|\\s]*)*?/?> *(?:\\n{2,}|\\s*$))").replace("comment",n._comment).replace(/tag/g,"(?!(?:a|em|strong|small|s|cite|q|dfn|abbr|data|time|code|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo|span|br|wbr|ins|del|img)\\b)\\w+(?!:|[^\\w\\s@]*@)\\b").getRegex(),def:/^ *\[([^\]]+)\]: *]+)>?(?: +(["(][^\n]+[")]))? *(?:\n+|$)/}),r.rules=n,r.lex=function(t,e){var n=new r(e);return n.lex(t)},r.prototype.lex=function(t){return t=t.replace(/\r\n|\r/g,"\n").replace(/\t/g," ").replace(/\u00a0/g," ").replace(/\u2424/g,"\n"),this.token(t,!0)},r.prototype.token=function(t,e){var r,i,o,a,u,s,c,l,f,p,h,d,v;t=t.replace(/^ +$/gm,"");while(t)if((o=this.rules.newline.exec(t))&&(t=t.substring(o[0].length),o[0].length>1&&this.tokens.push({type:"space"})),o=this.rules.code.exec(t))t=t.substring(o[0].length),o=o[0].replace(/^ {4}/gm,""),this.tokens.push({type:"code",text:this.options.pedantic?o:o.replace(/\n+$/,"")});else if(o=this.rules.fences.exec(t))t=t.substring(o[0].length),this.tokens.push({type:"code",lang:o[2],text:o[3]||""});else if(o=this.rules.heading.exec(t))t=t.substring(o[0].length),this.tokens.push({type:"heading",depth:o[1].length,text:o[2]});else if(e&&(o=this.rules.nptable.exec(t))&&(s={type:"table",header:g(o[1].replace(/^ *| *\| *$/g,"")),align:o[2].replace(/^ *|\| *$/g,"").split(/ *\| */),cells:o[3]?o[3].replace(/\n$/,"").split("\n"):[]},s.header.length===s.align.length)){for(t=t.substring(o[0].length),l=0;l ?/gm,""),this.token(o,e),this.tokens.push({type:"blockquote_end"});else if(o=this.rules.list.exec(t)){for(t=t.substring(o[0].length),a=o[2],h=a.length>1,this.tokens.push({type:"list_start",ordered:h,start:h?+a:""}),o=o[0].match(this.rules.item),r=!1,p=o.length,l=0;l1&&u.length>1||(t=o.slice(l+1).join("\n")+t,l=p-1)),i=r||/\n\n(?!\s*$)/.test(s),l!==p-1&&(r="\n"===s.charAt(s.length-1),i||(i=r)),d=/^\[[ xX]\] /.test(s),v=void 0,d&&(v=" "!==s[1],s=s.replace(/^\[[ xX]\] +/,"")),this.tokens.push({type:i?"loose_item_start":"list_item_start",task:d,checked:v}),this.token(s,!1),this.tokens.push({type:"list_item_end"});this.tokens.push({type:"list_end"})}else if(o=this.rules.html.exec(t))t=t.substring(o[0].length),this.tokens.push({type:this.options.sanitize?"paragraph":"html",pre:!this.options.sanitizer&&("pre"===o[1]||"script"===o[1]||"style"===o[1]),text:o[0]});else if(e&&(o=this.rules.def.exec(t)))t=t.substring(o[0].length),o[3]&&(o[3]=o[3].substring(1,o[3].length-1)),f=o[1].toLowerCase().replace(/\s+/g," "),this.tokens.links[f]||(this.tokens.links[f]={href:o[2],title:o[3]});else if(e&&(o=this.rules.table.exec(t))&&(s={type:"table",header:g(o[1].replace(/^ *| *\| *$/g,"")),align:o[2].replace(/^ *|\| *$/g,"").split(/ *\| */),cells:o[3]?o[3].replace(/(?: *\| *)?\n$/,"").split("\n"):[]},s.header.length===s.align.length)){for(t=t.substring(o[0].length),l=0;l?@\[\]\\^_`{|}~])/,autolink:/^<(scheme:[^\s\x00-\x1f<>]*|email)>/,url:v,tag:"^comment|^|^<[a-zA-Z][\\w-]*(?:attribute)*?\\s*/?>|^<\\?[\\s\\S]*?\\?>|^|^",link:/^!?\[(label)\]\(href(?:\s+(title))?\s*\)/,reflink:/^!?\[(label)\]\[(?!\s*\])((?:\\[\[\]]?|[^\[\]\\])+)\]/,nolink:/^!?\[(?!\s*\])((?:\[[^\[\]]*\]|\\[\[\]]|[^\[\]])*)\](?:\[\])?/,strong:/^__([^\s][\s\S]*?[^\s])__(?!_)|^\*\*([^\s][\s\S]*?[^\s])\*\*(?!\*)|^__([^\s])__(?!_)|^\*\*([^\s])\*\*(?!\*)/,em:/^_([^\s][\s\S]*?[^\s_])_(?!_)|^_([^\s_][\s\S]*?[^\s])_(?!_)|^\*([^\s][\s\S]*?[^\s*])\*(?!\*)|^\*([^\s*][\s\S]*?[^\s])\*(?!\*)|^_([^\s_])_(?!_)|^\*([^\s*])\*(?!\*)/,code:/^(`+)\s*([\s\S]*?[^`]?)\s*\1(?!`)/,br:/^ {2,}\n(?!\s*$)/,del:v,text:/^[\s\S]+?(?=[\\/g,">").replace(/"/g,""").replace(/'/g,"'")}function l(t){return t.replace(/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/gi,function(t,e){return e=e.toLowerCase(),"colon"===e?":":"#"===e.charAt(0)?"x"===e.charAt(1)?String.fromCharCode(parseInt(e.substring(2),16)):String.fromCharCode(+e.substring(1)):""})}function f(t,e){return t=t.source||t,e=e||"",{replace:function(e,n){return n=n.source||n,n=n.replace(/(^|[^\[])\^/g,"$1"),t=t.replace(e,n),this},getRegex:function(){return new RegExp(t,e)}}}function p(t,e){return h[" "+t]||(/^[^:]+:\/*[^/]*$/.test(t)?h[" "+t]=t+"/":h[" "+t]=t.replace(/[^/]*$/,"")),t=h[" "+t],"//"===e.slice(0,2)?t.replace(/:[\s\S]*/,":")+e:"/"===e.charAt(0)?t.replace(/(:\/*[^/]*)[\s\S]*/,"$1")+e:t+e}i._escapes=/\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/g,i._scheme=/[a-zA-Z][a-zA-Z0-9+.-]{1,31}/,i._email=/[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+(@)[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+(?![-_])/,i.autolink=f(i.autolink).replace("scheme",i._scheme).replace("email",i._email).getRegex(),i._attribute=/\s+[a-zA-Z:_][\w.:-]*(?:\s*=\s*"[^"]*"|\s*=\s*'[^']*'|\s*=\s*[^\s"'=<>`]+)?/,i.tag=f(i.tag).replace("comment",n._comment).replace("attribute",i._attribute).getRegex(),i._label=/(?:\[[^\[\]]*\]|\\[\[\]]?|`[^`]*`|[^\[\]\\])*?/,i._href=/\s*(<(?:\\[<>]?|[^\s<>\\])*>|(?:\\[()]?|\([^\s\x00-\x1f()\\]*\)|[^\s\x00-\x1f()\\])*?)/,i._title=/"(?:\\"?|[^"\\])*"|'(?:\\'?|[^'\\])*'|\((?:\\\)?|[^)\\])*\)/,i.link=f(i.link).replace("label",i._label).replace("href",i._href).replace("title",i._title).getRegex(),i.reflink=f(i.reflink).replace("label",i._label).getRegex(),i.normal=y({},i),i.pedantic=y({},i.normal,{strong:/^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,em:/^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/,link:f(/^!?\[(label)\]\((.*?)\)/).replace("label",i._label).getRegex(),reflink:f(/^!?\[(label)\]\s*\[([^\]]*)\]/).replace("label",i._label).getRegex()}),i.gfm=y({},i.normal,{escape:f(i.escape).replace("])","~|])").getRegex(),url:f(/^((?:ftp|https?):\/\/|www\.)(?:[a-zA-Z0-9\-]+\.?)+[^\s<]*|^email/).replace("email",i._email).getRegex(),_backpedal:/(?:[^?!.,:;*_~()&]+|\([^)]*\)|&(?![a-zA-Z0-9]+;$)|[?!.,:;*_~)]+(?!$))+/,del:/^~~(?=\S)([\s\S]*?\S)~~/,text:f(i.text).replace("]|","~]|").replace("|","|https?://|ftp://|www\\.|[a-zA-Z0-9.!#$%&'*+/=?^_`{\\|}~-]+@|").getRegex()}),i.breaks=y({},i.gfm,{br:f(i.br).replace("{2,}","*").getRegex(),text:f(i.gfm.text).replace("{2,}","*").getRegex()}),o.rules=i,o.output=function(t,e,n){var r=new o(e,n);return r.output(t)},o.prototype.output=function(t){var e,n,r,i,a,u="";while(t)if(a=this.rules.escape.exec(t))t=t.substring(a[0].length),u+=a[1];else if(a=this.rules.autolink.exec(t))t=t.substring(a[0].length),"@"===a[2]?(n=c(this.mangle(a[1])),r="mailto:"+n):(n=c(a[1]),r=n),u+=this.renderer.link(r,null,n);else if(this.inLink||!(a=this.rules.url.exec(t))){if(a=this.rules.tag.exec(t))!this.inLink&&/^/i.test(a[0])&&(this.inLink=!1),t=t.substring(a[0].length),u+=this.options.sanitize?this.options.sanitizer?this.options.sanitizer(a[0]):c(a[0]):a[0];else if(a=this.rules.link.exec(t))t=t.substring(a[0].length),this.inLink=!0,r=a[2],this.options.pedantic?(e=/^([^'"]*[^\s])\s+(['"])(.*)\2/.exec(r),e?(r=e[1],i=e[3]):i=""):i=a[3]?a[3].slice(1,-1):"",r=r.trim().replace(/^<([\s\S]*)>$/,"$1"),u+=this.outputLink(a,{href:o.escapes(r),title:o.escapes(i)}),this.inLink=!1;else if((a=this.rules.reflink.exec(t))||(a=this.rules.nolink.exec(t))){if(t=t.substring(a[0].length),e=(a[2]||a[1]).replace(/\s+/g," "),e=this.links[e.toLowerCase()],!e||!e.href){u+=a[0].charAt(0),t=a[0].substring(1)+t;continue}this.inLink=!0,u+=this.outputLink(a,e),this.inLink=!1}else if(a=this.rules.strong.exec(t))t=t.substring(a[0].length),u+=this.renderer.strong(this.output(a[4]||a[3]||a[2]||a[1]));else if(a=this.rules.em.exec(t))t=t.substring(a[0].length),u+=this.renderer.em(this.output(a[6]||a[5]||a[4]||a[3]||a[2]||a[1]));else if(a=this.rules.code.exec(t))t=t.substring(a[0].length),u+=this.renderer.codespan(c(a[2].trim(),!0));else if(a=this.rules.br.exec(t))t=t.substring(a[0].length),u+=this.renderer.br();else if(a=this.rules.del.exec(t))t=t.substring(a[0].length),u+=this.renderer.del(this.output(a[1]));else if(a=this.rules.text.exec(t))t=t.substring(a[0].length),u+=this.renderer.text(c(this.smartypants(a[0])));else if(t)throw new Error("Infinite loop on byte: "+t.charCodeAt(0))}else a[0]=this.rules._backpedal.exec(a[0])[0],t=t.substring(a[0].length),"@"===a[2]?(n=c(a[0]),r="mailto:"+n):(n=c(a[0]),r="www."===a[1]?"http://"+n:n),u+=this.renderer.link(r,null,n);return u},o.escapes=function(t){return t?t.replace(o.rules._escapes,"$1"):t},o.prototype.outputLink=function(t,e){var n=e.href,r=e.title?c(e.title):null;return"!"!==t[0].charAt(0)?this.renderer.link(n,r,this.output(t[1])):this.renderer.image(n,r,c(t[1]))},o.prototype.smartypants=function(t){return this.options.smartypants?t.replace(/---/g,"—").replace(/--/g,"–").replace(/(^|[-\u2014/(\[{"\s])'/g,"$1‘").replace(/'/g,"’").replace(/(^|[-\u2014/(\[{\u2018\s])"/g,"$1“").replace(/"/g,"”").replace(/\.{3}/g,"…"):t},o.prototype.mangle=function(t){if(!this.options.mangle)return t;for(var e,n="",r=t.length,i=0;i.5&&(e="x"+e.toString(16)),n+="&#"+e+";";return n},a.prototype.code=function(t,e,n){if(this.options.highlight){var r=this.options.highlight(t,e);null!=r&&r!==t&&(n=!0,t=r)}return e?'
'+(n?t:c(t,!0))+"
\n":"
"+(n?t:c(t,!0))+"
"},a.prototype.blockquote=function(t){return"
\n"+t+"
\n"},a.prototype.html=function(t){return t},a.prototype.heading=function(t,e,n){return this.options.headerIds?"'+t+"\n":""+t+"\n"},a.prototype.hr=function(){return this.options.xhtml?"
\n":"
\n"},a.prototype.list=function(t,e,n){var r=e?"ol":"ul",i=e&&1!==n?' start="'+n+'"':"";return"<"+r+i+">\n"+t+"\n"},a.prototype.listitem=function(t){return"
  • "+t+"
  • \n"},a.prototype.checkbox=function(t){return" "},a.prototype.paragraph=function(t){return"

    "+t+"

    \n"},a.prototype.table=function(t,e){return e&&(e=""+e+""),"\n\n"+t+"\n"+e+"
    \n"},a.prototype.tablerow=function(t){return"\n"+t+"\n"},a.prototype.tablecell=function(t,e){var n=e.header?"th":"td",r=e.align?"<"+n+' align="'+e.align+'">':"<"+n+">";return r+t+"\n"},a.prototype.strong=function(t){return""+t+""},a.prototype.em=function(t){return""+t+""},a.prototype.codespan=function(t){return""+t+""},a.prototype.br=function(){return this.options.xhtml?"
    ":"
    "},a.prototype.del=function(t){return""+t+""},a.prototype.link=function(t,e,n){if(this.options.sanitize){try{var r=decodeURIComponent(l(t)).replace(/[^\w:]/g,"").toLowerCase()}catch(t){return n}if(0===r.indexOf("javascript:")||0===r.indexOf("vbscript:")||0===r.indexOf("data:"))return n}this.options.baseUrl&&!d.test(t)&&(t=p(this.options.baseUrl,t));try{t=encodeURI(t).replace(/%25/g,"%")}catch(t){return n}var i='
    ",i},a.prototype.image=function(t,e,n){this.options.baseUrl&&!d.test(t)&&(t=p(this.options.baseUrl,t));var r=''+n+'":">",r},a.prototype.text=function(t){return t},u.prototype.strong=u.prototype.em=u.prototype.codespan=u.prototype.del=u.prototype.text=function(t){return t},u.prototype.link=u.prototype.image=function(t,e,n){return""+n},u.prototype.br=function(){return""},s.parse=function(t,e){var n=new s(e);return n.parse(t)},s.prototype.parse=function(t){this.inline=new o(t.links,this.options),this.inlineText=new o(t.links,y({},this.options,{renderer:new u})),this.tokens=t.reverse();var e="";while(this.next())e+=this.tok();return e},s.prototype.next=function(){return this.token=this.tokens.pop()},s.prototype.peek=function(){return this.tokens[this.tokens.length-1]||0},s.prototype.parseText=function(){var t=this.token.text;while("text"===this.peek().type)t+="\n"+this.next().text;return this.inline.output(t)},s.prototype.tok=function(){switch(this.token.type){case"space":return"";case"hr":return this.renderer.hr();case"heading":return this.renderer.heading(this.inline.output(this.token.text),this.token.depth,l(this.inlineText.output(this.token.text)));case"code":return this.renderer.code(this.token.text,this.token.lang,this.token.escaped);case"table":var t,e,n,r,i="",o="";for(n="",t=0;te)n.splice(e);else while(n.lengthAn error occurred:

    "+c(t.message+"",!0)+"
    ";throw t}}v.exec=v,_.options=_.setOptions=function(t){return y(_.defaults,t),_},_.getDefaults=function(){return{baseUrl:null,breaks:!1,gfm:!0,headerIds:!0,headerPrefix:"",highlight:null,langPrefix:"language-",mangle:!0,pedantic:!1,renderer:new a,sanitize:!1,sanitizer:null,silent:!1,smartLists:!1,smartypants:!1,tables:!0,xhtml:!1}},_.defaults=_.getDefaults(),_.Parser=s,_.parser=s.parse,_.Renderer=a,_.TextRenderer=u,_.Lexer=r,_.lexer=r.lex,_.InlineLexer=o,_.inlineLexer=o.output,_.parse=_,t.exports=_})(this||"undefined"!==typeof window&&window)}).call(this,n("yLpj"))},EWmC:function(t,e,n){var r=n("LZWt");t.exports=Array.isArray||function(t){return"Array"==r(t)}},FJW5:function(t,e,n){var r=n("hswa"),i=n("y3w9"),o=n("DVgA");t.exports=n("nh4g")?Object.defineProperties:function(t,e){i(t);var n,a=o(e),u=a.length,s=0;while(u>s)r.f(t,n=a[s++],e[n]);return t}},GZEu:function(t,e,n){var r,i,o,a=n("m0Pp"),u=n("MfQN"),s=n("+rLv"),c=n("Iw71"),l=n("dyZX"),f=l.process,p=l.setImmediate,h=l.clearImmediate,d=l.MessageChannel,v=l.Dispatch,y=0,g={},_="onreadystatechange",m=function(){var t=+this;if(g.hasOwnProperty(t)){var e=g[t];delete g[t],e()}},b=function(t){m.call(t.data)};p&&h||(p=function(t){var e=[],n=1;while(arguments.length>n)e.push(arguments[n++]);return g[++y]=function(){u("function"==typeof t?t:Function(t),e)},r(y),y},h=function(t){delete g[t]},"process"==n("LZWt")(f)?r=function(t){f.nextTick(a(m,t,1))}:v&&v.now?r=function(t){v.now(a(m,t,1))}:d?(i=new d,o=i.port2,i.port1.onmessage=b,r=a(o.postMessage,o,1)):l.addEventListener&&"function"==typeof postMessage&&!l.importScripts?(r=function(t){l.postMessage(t+"","*")},l.addEventListener("message",b,!1)):r=_ in c("script")?function(t){s.appendChild(c("script"))[_]=function(){s.removeChild(this),m.call(t)}}:function(t){setTimeout(a(m,t,1),0)}),t.exports={set:p,clear:h}},H6hf:function(t,e,n){var r=n("y3w9");t.exports=function(t,e,n,i){try{return i?e(r(n)[0],n[1]):e(n)}catch(e){var o=t["return"];throw void 0!==o&&r(o.call(t)),e}}},"I8a+":function(t,e,n){var r=n("LZWt"),i=n("K0xU")("toStringTag"),o="Arguments"==r(function(){return arguments}()),a=function(t,e){try{return t[e]}catch(t){}};t.exports=function(t){var e,n,u;return void 0===t?"Undefined":null===t?"Null":"string"==typeof(n=a(e=Object(t),i))?n:o?r(e):"Object"==(u=r(e))&&"function"==typeof e.callee?"Arguments":u}},INYr:function(t,e,n){"use strict";var r=n("XKFU"),i=n("CkkT")(6),o="findIndex",a=!0;o in[]&&Array(1)[o](function(){a=!1}),r(r.P+r.F*a,"Array",{findIndex:function(t){return i(this,t,arguments.length>1?arguments[1]:void 0)}}),n("nGyu")(o)},Iw71:function(t,e,n){var r=n("0/R4"),i=n("dyZX").document,o=r(i)&&r(i.createElement);t.exports=function(t){return o?i.createElement(t):{}}},"J+6e":function(t,e,n){var r=n("I8a+"),i=n("K0xU")("iterator"),o=n("hPIQ");t.exports=n("g3g5").getIteratorMethod=function(t){if(void 0!=t)return t[i]||t["@@iterator"]||o[r(t)]}},JiEa:function(t,e){e.f=Object.getOwnPropertySymbols},K0xU:function(t,e,n){var r=n("VTer")("wks"),i=n("ylqs"),o=n("dyZX").Symbol,a="function"==typeof o,u=t.exports=function(t){return r[t]||(r[t]=a&&o[t]||(a?o:i)("Symbol."+t))};u.store=r},"KHd+":function(t,e,n){"use strict";function r(t,e,n,r,i,o,a,u){var s,c="function"===typeof t?t.options:t;if(e&&(c.render=e,c.staticRenderFns=n,c._compiled=!0),r&&(c.functional=!0),o&&(c._scopeId="data-v-"+o),a?(s=function(t){t=t||this.$vnode&&this.$vnode.ssrContext||this.parent&&this.parent.$vnode&&this.parent.$vnode.ssrContext,t||"undefined"===typeof __VUE_SSR_CONTEXT__||(t=__VUE_SSR_CONTEXT__),i&&i.call(this,t),t&&t._registeredComponents&&t._registeredComponents.add(a)},c._ssrRegister=s):i&&(s=u?function(){i.call(this,this.$root.$options.shadowRoot)}:i),s)if(c.functional){c._injectStyles=s;var l=c.render;c.render=function(t,e){return s.call(e),l(t,e)}}else{var f=c.beforeCreate;c.beforeCreate=f?[].concat(f,s):[s]}return{exports:t,options:c}}n.d(e,"a",function(){return r})},KroJ:function(t,e,n){var r=n("dyZX"),i=n("Mukb"),o=n("aagx"),a=n("ylqs")("src"),u="toString",s=Function[u],c=(""+s).split(u);n("g3g5").inspectSource=function(t){return s.call(t)},(t.exports=function(t,e,n,u){var s="function"==typeof n;s&&(o(n,"name")||i(n,"name",e)),t[e]!==n&&(s&&(o(n,a)||i(n,a,t[e]?""+t[e]:c.join(String(e)))),t===r?t[e]=n:u?t[e]?t[e]=n:i(t,e,n):(delete t[e],i(t,e,n)))})(Function.prototype,u,function(){return"function"==typeof this&&this[a]||s.call(this)})},Kuth:function(t,e,n){var r=n("y3w9"),i=n("FJW5"),o=n("4R4u"),a=n("YTvA")("IE_PROTO"),u=function(){},s="prototype",c=function(){var t,e=n("Iw71")("iframe"),r=o.length,i="<",a=">";e.style.display="none",n("+rLv").appendChild(e),e.src="javascript:",t=e.contentWindow.document,t.open(),t.write(i+"script"+a+"document.F=Object"+i+"/script"+a),t.close(),c=t.F;while(r--)delete c[s][o[r]];return c()};t.exports=Object.create||function(t,e){var n;return null!==t?(u[s]=r(t),n=new u,u[s]=null,n[a]=t):n=c(),void 0===e?n:i(n,e)}},Kw5r:function(t,e,n){"use strict";(function(t){ +/*! + * Vue.js v2.5.16 + * (c) 2014-2018 Evan You + * Released under the MIT License. + */ +var n=Object.freeze({});function r(t){return void 0===t||null===t}function i(t){return void 0!==t&&null!==t}function o(t){return!0===t}function a(t){return!1===t}function u(t){return"string"===typeof t||"number"===typeof t||"symbol"===typeof t||"boolean"===typeof t}function s(t){return null!==t&&"object"===typeof t}var c=Object.prototype.toString;function l(t){return"[object Object]"===c.call(t)}function f(t){return"[object RegExp]"===c.call(t)}function p(t){var e=parseFloat(String(t));return e>=0&&Math.floor(e)===e&&isFinite(t)}function h(t){return null==t?"":"object"===typeof t?JSON.stringify(t,null,2):String(t)}function d(t){var e=parseFloat(t);return isNaN(e)?t:e}function v(t,e){for(var n=Object.create(null),r=t.split(","),i=0;i-1)return t.splice(n,1)}}var _=Object.prototype.hasOwnProperty;function m(t,e){return _.call(t,e)}function b(t){var e=Object.create(null);return function(n){var r=e[n];return r||(e[n]=t(n))}}var w=/-(\w)/g,x=b(function(t){return t.replace(w,function(t,e){return e?e.toUpperCase():""})}),k=b(function(t){return t.charAt(0).toUpperCase()+t.slice(1)}),A=/\B([A-Z])/g,O=b(function(t){return t.replace(A,"-$1").toLowerCase()});function C(t,e){function n(n){var r=arguments.length;return r?r>1?t.apply(e,arguments):t.call(e,n):t.call(e)}return n._length=t.length,n}function E(t,e){return t.bind(e)}var j=Function.prototype.bind?E:C;function S(t,e){e=e||0;var n=t.length-e,r=new Array(n);while(n--)r[n]=t[n+e];return r}function $(t,e){for(var n in e)t[n]=e[n];return t}function T(t){for(var e={},n=0;n0,tt=J&&J.indexOf("edge/")>0,et=(J&&J.indexOf("android"),J&&/iphone|ipad|ipod|ios/.test(J)||"ios"===G),nt=(J&&/chrome\/\d+/.test(J),{}.watch),rt=!1;if(Z)try{var it={};Object.defineProperty(it,"passive",{get:function(){rt=!0}}),window.addEventListener("test-passive",null,it)}catch(t){}var ot=function(){return void 0===K&&(K=!Z&&!X&&"undefined"!==typeof t&&"server"===t["process"].env.VUE_ENV),K},at=Z&&window.__VUE_DEVTOOLS_GLOBAL_HOOK__;function ut(t){return"function"===typeof t&&/native code/.test(t.toString())}var st,ct="undefined"!==typeof Symbol&&ut(Symbol)&&"undefined"!==typeof Reflect&&ut(Reflect.ownKeys);st="undefined"!==typeof Set&&ut(Set)?Set:function(){function t(){this.set=Object.create(null)}return t.prototype.has=function(t){return!0===this.set[t]},t.prototype.add=function(t){this.set[t]=!0},t.prototype.clear=function(){this.set=Object.create(null)},t}();var lt=R,ft=0,pt=function(){this.id=ft++,this.subs=[]};pt.prototype.addSub=function(t){this.subs.push(t)},pt.prototype.removeSub=function(t){g(this.subs,t)},pt.prototype.depend=function(){pt.target&&pt.target.addDep(this)},pt.prototype.notify=function(){for(var t=this.subs.slice(),e=0,n=t.length;e-1)if(o&&!m(i,"default"))a=!1;else if(""===a||a===O(t)){var s=Gt(String,i.type);(s<0||u0&&(a=Ae(a,(e||"")+"_"+n),ke(a[0])&&ke(c)&&(l[s]=mt(c.text+a[0].text),a.shift()),l.push.apply(l,a)):u(a)?ke(c)?l[s]=mt(c.text+a):""!==a&&l.push(mt(a)):ke(a)&&ke(c)?l[s]=mt(c.text+a.text):(o(t._isVList)&&i(a.tag)&&r(a.key)&&i(e)&&(a.key="__vlist"+e+"_"+n+"__"),l.push(a)));return l}function Oe(t,e){return(t.__esModule||ct&&"Module"===t[Symbol.toStringTag])&&(t=t.default),s(t)?e.extend(t):t}function Ce(t,e,n,r,i){var o=_t();return o.asyncFactory=t,o.asyncMeta={data:e,context:n,children:r,tag:i},o}function Ee(t,e,n){if(o(t.error)&&i(t.errorComp))return t.errorComp;if(i(t.resolved))return t.resolved;if(o(t.loading)&&i(t.loadingComp))return t.loadingComp;if(!i(t.contexts)){var a=t.contexts=[n],u=!0,c=function(){for(var t=0,e=a.length;t1?S(n):n;for(var r=S(arguments,1),i=0,o=n.length;iYe&&Ve[n].id>t.id)n--;Ve.splice(n+1,0,t)}else Ve.push(t);Ge||(Ge=!0,le(tn))}}var an=0,un=function(t,e,n,r,i){this.vm=t,i&&(t._watcher=this),t._watchers.push(this),r?(this.deep=!!r.deep,this.user=!!r.user,this.lazy=!!r.lazy,this.sync=!!r.sync):this.deep=this.user=this.lazy=this.sync=!1,this.cb=n,this.id=++an,this.active=!0,this.dirty=this.lazy,this.deps=[],this.newDeps=[],this.depIds=new st,this.newDepIds=new st,this.expression="","function"===typeof e?this.getter=e:(this.getter=B(e),this.getter||(this.getter=function(){})),this.value=this.lazy?void 0:this.get()};un.prototype.get=function(){var t;dt(this);var e=this.vm;try{t=this.getter.call(e,e)}catch(t){if(!this.user)throw t;Jt(t,e,'getter for watcher "'+this.expression+'"')}finally{this.deep&&pe(t),vt(),this.cleanupDeps()}return t},un.prototype.addDep=function(t){var e=t.id;this.newDepIds.has(e)||(this.newDepIds.add(e),this.newDeps.push(t),this.depIds.has(e)||t.addSub(this))},un.prototype.cleanupDeps=function(){var t=this,e=this.deps.length;while(e--){var n=t.deps[e];t.newDepIds.has(n.id)||n.removeSub(t)}var r=this.depIds;this.depIds=this.newDepIds,this.newDepIds=r,this.newDepIds.clear(),r=this.deps,this.deps=this.newDeps,this.newDeps=r,this.newDeps.length=0},un.prototype.update=function(){this.lazy?this.dirty=!0:this.sync?this.run():on(this)},un.prototype.run=function(){if(this.active){var t=this.get();if(t!==this.value||s(t)||this.deep){var e=this.value;if(this.value=t,this.user)try{this.cb.call(this.vm,t,e)}catch(t){Jt(t,this.vm,'callback for watcher "'+this.expression+'"')}else this.cb.call(this.vm,t,e)}}},un.prototype.evaluate=function(){this.value=this.get(),this.dirty=!1},un.prototype.depend=function(){var t=this,e=this.deps.length;while(e--)t.deps[e].depend()},un.prototype.teardown=function(){var t=this;if(this.active){this.vm._isBeingDestroyed||g(this.vm._watchers,this);var e=this.deps.length;while(e--)t.deps[e].removeSub(t);this.active=!1}};var sn={enumerable:!0,configurable:!0,get:R,set:R};function cn(t,e,n){sn.get=function(){return this[e][n]},sn.set=function(t){this[e][n]=t},Object.defineProperty(t,n,sn)}function ln(t){t._watchers=[];var e=t.$options;e.props&&fn(t,e.props),e.methods&&_n(t,e.methods),e.data?pn(t):$t(t._data={},!0),e.computed&&vn(t,e.computed),e.watch&&e.watch!==nt&&mn(t,e.watch)}function fn(t,e){var n=t.$options.propsData||{},r=t._props={},i=t.$options._propKeys=[],o=!t.$parent;o||Ct(!1);var a=function(o){i.push(o);var a=Kt(o,e,n,t);Tt(r,o,a),o in t||cn(t,"_props",o)};for(var u in e)a(u);Ct(!0)}function pn(t){var e=t.$options.data;e=t._data="function"===typeof e?hn(e,t):e||{},l(e)||(e={});var n=Object.keys(e),r=t.$options.props,i=(t.$options.methods,n.length);while(i--){var o=n[i];0,r&&m(r,o)||F(o)||cn(t,"_data",o)}$t(e,!0)}function hn(t,e){dt();try{return t.call(e,e)}catch(t){return Jt(t,e,"data()"),{}}finally{vt()}}var dn={lazy:!0};function vn(t,e){var n=t._computedWatchers=Object.create(null),r=ot();for(var i in e){var o=e[i],a="function"===typeof o?o:o.get;0,r||(n[i]=new un(t,a||R,R,dn)),i in t||yn(t,i,o)}}function yn(t,e,n){var r=!ot();"function"===typeof n?(sn.get=r?gn(e):n,sn.set=R):(sn.get=n.get?r&&!1!==n.cache?gn(e):n.get:R,sn.set=n.set?n.set:R),Object.defineProperty(t,e,sn)}function gn(t){return function(){var e=this._computedWatchers&&this._computedWatchers[t];if(e)return e.dirty&&e.evaluate(),pt.target&&e.depend(),e.value}}function _n(t,e){t.$options.props;for(var n in e)t[n]=null==e[n]?R:j(e[n],t)}function mn(t,e){for(var n in e){var r=e[n];if(Array.isArray(r))for(var i=0;i=0||n.indexOf(t[i])<0)&&r.push(t[i]);return r}return t}function ur(t){this._init(t)}function sr(t){t.use=function(t){var e=this._installedPlugins||(this._installedPlugins=[]);if(e.indexOf(t)>-1)return this;var n=S(arguments,1);return n.unshift(this),"function"===typeof t.install?t.install.apply(t,n):"function"===typeof t&&t.apply(null,n),e.push(t),this}}function cr(t){t.mixin=function(t){return this.options=Wt(this.options,t),this}}function lr(t){t.cid=0;var e=1;t.extend=function(t){t=t||{};var n=this,r=n.cid,i=t._Ctor||(t._Ctor={});if(i[r])return i[r];var o=t.name||n.options.name;var a=function(t){this._init(t)};return a.prototype=Object.create(n.prototype),a.prototype.constructor=a,a.cid=e++,a.options=Wt(n.options,t),a["super"]=n,a.options.props&&fr(a),a.options.computed&&pr(a),a.extend=n.extend,a.mixin=n.mixin,a.use=n.use,z.forEach(function(t){a[t]=n[t]}),o&&(a.options.components[o]=a),a.superOptions=n.options,a.extendOptions=t,a.sealedOptions=$({},a.options),i[r]=a,a}}function fr(t){var e=t.options.props;for(var n in e)cn(t.prototype,"_props",n)}function pr(t){var e=t.options.computed;for(var n in e)yn(t.prototype,n,e[n])}function hr(t){z.forEach(function(e){t[e]=function(t,n){return n?("component"===e&&l(n)&&(n.name=n.name||t,n=this.options._base.extend(n)),"directive"===e&&"function"===typeof n&&(n={bind:n,update:n}),this.options[e+"s"][t]=n,n):this.options[e+"s"][t]}})}function dr(t){return t&&(t.Ctor.options.name||t.tag)}function vr(t,e){return Array.isArray(t)?t.indexOf(e)>-1:"string"===typeof t?t.split(",").indexOf(e)>-1:!!f(t)&&t.test(e)}function yr(t,e){var n=t.cache,r=t.keys,i=t._vnode;for(var o in n){var a=n[o];if(a){var u=dr(a.componentOptions);u&&!e(u)&&gr(n,o,r,i)}}}function gr(t,e,n,r){var i=t[e];!i||r&&i.tag===r.tag||i.componentInstance.$destroy(),t[e]=null,g(n,e)}nr(ur),wn(ur),Ie(ur),qe(ur),tr(ur);var _r=[String,RegExp,Array],mr={name:"keep-alive",abstract:!0,props:{include:_r,exclude:_r,max:[String,Number]},created:function(){this.cache=Object.create(null),this.keys=[]},destroyed:function(){var t=this;for(var e in t.cache)gr(t.cache,e,t.keys)},mounted:function(){var t=this;this.$watch("include",function(e){yr(t,function(t){return vr(e,t)})}),this.$watch("exclude",function(e){yr(t,function(t){return!vr(e,t)})})},render:function(){var t=this.$slots.default,e=Se(t),n=e&&e.componentOptions;if(n){var r=dr(n),i=this,o=i.include,a=i.exclude;if(o&&(!r||!vr(o,r))||a&&r&&vr(a,r))return e;var u=this,s=u.cache,c=u.keys,l=null==e.key?n.Ctor.cid+(n.tag?"::"+n.tag:""):e.key;s[l]?(e.componentInstance=s[l].componentInstance,g(c,l),c.push(l)):(s[l]=e,c.push(l),this.max&&c.length>parseInt(this.max)&&gr(s,c[0],c,this._vnode)),e.data.keepAlive=!0}return e||t&&t[0]}},br={KeepAlive:mr};function wr(t){var e={get:function(){return N}};Object.defineProperty(t,"config",e),t.util={warn:lt,extend:$,mergeOptions:Wt,defineReactive:Tt},t.set=Rt,t.delete=Lt,t.nextTick=le,t.options=Object.create(null),z.forEach(function(e){t.options[e+"s"]=Object.create(null)}),t.options._base=t,$(t.options.components,br),sr(t),cr(t),lr(t),hr(t)}wr(ur),Object.defineProperty(ur.prototype,"$isServer",{get:ot}),Object.defineProperty(ur.prototype,"$ssrContext",{get:function(){return this.$vnode&&this.$vnode.ssrContext}}),Object.defineProperty(ur,"FunctionalRenderContext",{value:Dn}),ur.version="2.5.16";var xr=v("style,class"),kr=v("input,textarea,option,select,progress"),Ar=function(t,e,n){return"value"===n&&kr(t)&&"button"!==e||"selected"===n&&"option"===t||"checked"===n&&"input"===t||"muted"===n&&"video"===t},Or=v("contenteditable,draggable,spellcheck"),Cr=v("allowfullscreen,async,autofocus,autoplay,checked,compact,controls,declare,default,defaultchecked,defaultmuted,defaultselected,defer,disabled,enabled,formnovalidate,hidden,indeterminate,inert,ismap,itemscope,loop,multiple,muted,nohref,noresize,noshade,novalidate,nowrap,open,pauseonexit,readonly,required,reversed,scoped,seamless,selected,sortable,translate,truespeed,typemustmatch,visible"),Er="http://www.w3.org/1999/xlink",jr=function(t){return":"===t.charAt(5)&&"xlink"===t.slice(0,5)},Sr=function(t){return jr(t)?t.slice(6,t.length):""},$r=function(t){return null==t||!1===t};function Tr(t){var e=t.data,n=t,r=t;while(i(r.componentInstance))r=r.componentInstance._vnode,r&&r.data&&(e=Rr(r.data,e));while(i(n=n.parent))n&&n.data&&(e=Rr(e,n.data));return Lr(e.staticClass,e.class)}function Rr(t,e){return{staticClass:Ir(t.staticClass,e.staticClass),class:i(t.class)?[t.class,e.class]:e.class}}function Lr(t,e){return i(t)||i(e)?Ir(t,Pr(e)):""}function Ir(t,e){return t?e?t+" "+e:t:e||""}function Pr(t){return Array.isArray(t)?Mr(t):s(t)?Dr(t):"string"===typeof t?t:""}function Mr(t){for(var e,n="",r=0,o=t.length;r-1?Hr[t]=e.constructor===window.HTMLUnknownElement||e.constructor===window.HTMLElement:Hr[t]=/HTMLUnknownElement/.test(e.toString())}var Br=v("text,number,password,search,email,tel,url");function Kr(t){if("string"===typeof t){var e=document.querySelector(t);return e||document.createElement("div")}return t}function Vr(t,e){var n=document.createElement(t);return"select"!==t?n:(e.data&&e.data.attrs&&void 0!==e.data.attrs.multiple&&n.setAttribute("multiple","multiple"),n)}function Zr(t,e){return document.createElementNS(Ur[t],e)}function Xr(t){return document.createTextNode(t)}function Gr(t){return document.createComment(t)}function Jr(t,e,n){t.insertBefore(e,n)}function Yr(t,e){t.removeChild(e)}function Qr(t,e){t.appendChild(e)}function ti(t){return t.parentNode}function ei(t){return t.nextSibling}function ni(t){return t.tagName}function ri(t,e){t.textContent=e}function ii(t,e){t.setAttribute(e,"")}var oi=Object.freeze({createElement:Vr,createElementNS:Zr,createTextNode:Xr,createComment:Gr,insertBefore:Jr,removeChild:Yr,appendChild:Qr,parentNode:ti,nextSibling:ei,tagName:ni,setTextContent:ri,setStyleScope:ii}),ai={create:function(t,e){ui(e)},update:function(t,e){t.data.ref!==e.data.ref&&(ui(t,!0),ui(e))},destroy:function(t){ui(t,!0)}};function ui(t,e){var n=t.data.ref;if(i(n)){var r=t.context,o=t.componentInstance||t.elm,a=r.$refs;e?Array.isArray(a[n])?g(a[n],o):a[n]===o&&(a[n]=void 0):t.data.refInFor?Array.isArray(a[n])?a[n].indexOf(o)<0&&a[n].push(o):a[n]=[o]:a[n]=o}}var si=new yt("",{},[]),ci=["create","activate","update","remove","destroy"];function li(t,e){return t.key===e.key&&(t.tag===e.tag&&t.isComment===e.isComment&&i(t.data)===i(e.data)&&fi(t,e)||o(t.isAsyncPlaceholder)&&t.asyncFactory===e.asyncFactory&&r(e.asyncFactory.error))}function fi(t,e){if("input"!==t.tag)return!0;var n,r=i(n=t.data)&&i(n=n.attrs)&&n.type,o=i(n=e.data)&&i(n=n.attrs)&&n.type;return r===o||Br(r)&&Br(o)}function pi(t,e,n){var r,o,a={};for(r=e;r<=n;++r)o=t[r].key,i(o)&&(a[o]=r);return a}function hi(t){var e,n,a={},s=t.modules,c=t.nodeOps;for(e=0;ev?(f=r(n[_+1])?null:n[_+1].elm,k(t,f,n,d,_,o)):d>_&&O(t,e,p,v)}function j(t,e,n,r){for(var o=n;o-1?Ai(t,e,n):Cr(e)?$r(n)?t.removeAttribute(e):(n="allowfullscreen"===e&&"EMBED"===t.tagName?"true":e,t.setAttribute(e,n)):Or(e)?t.setAttribute(e,$r(n)||"false"===n?"false":"true"):jr(e)?$r(n)?t.removeAttributeNS(Er,Sr(e)):t.setAttributeNS(Er,e,n):Ai(t,e,n)}function Ai(t,e,n){if($r(n))t.removeAttribute(e);else{if(Y&&!Q&&"TEXTAREA"===t.tagName&&"placeholder"===e&&!t.__ieph){var r=function(e){e.stopImmediatePropagation(),t.removeEventListener("input",r)};t.addEventListener("input",r),t.__ieph=!0}t.setAttribute(e,n)}}var Oi={create:xi,update:xi};function Ci(t,e){var n=e.elm,o=e.data,a=t.data;if(!(r(o.staticClass)&&r(o.class)&&(r(a)||r(a.staticClass)&&r(a.class)))){var u=Tr(e),s=n._transitionClasses;i(s)&&(u=Ir(u,Pr(s))),u!==n._prevClass&&(n.setAttribute("class",u),n._prevClass=u)}}var Ei,ji={create:Ci,update:Ci},Si="__r",$i="__c";function Ti(t){if(i(t[Si])){var e=Y?"change":"input";t[e]=[].concat(t[Si],t[e]||[]),delete t[Si]}i(t[$i])&&(t.change=[].concat(t[$i],t.change||[]),delete t[$i])}function Ri(t,e,n){var r=Ei;return function i(){var o=t.apply(null,arguments);null!==o&&Ii(e,i,n,r)}}function Li(t,e,n,r,i){e=ce(e),n&&(e=Ri(e,t,r)),Ei.addEventListener(t,e,rt?{capture:r,passive:i}:r)}function Ii(t,e,n,r){(r||Ei).removeEventListener(t,e._withTask||e,n)}function Pi(t,e){if(!r(t.data.on)||!r(e.data.on)){var n=e.data.on||{},i=t.data.on||{};Ei=e.elm,Ti(n),ge(n,i,Li,Ii,e.context),Ei=void 0}}var Mi={create:Pi,update:Pi};function Di(t,e){if(!r(t.data.domProps)||!r(e.data.domProps)){var n,o,a=e.elm,u=t.data.domProps||{},s=e.data.domProps||{};for(n in i(s.__ob__)&&(s=e.data.domProps=$({},s)),u)r(s[n])&&(a[n]="");for(n in s){if(o=s[n],"textContent"===n||"innerHTML"===n){if(e.children&&(e.children.length=0),o===u[n])continue;1===a.childNodes.length&&a.removeChild(a.childNodes[0])}if("value"===n){a._value=o;var c=r(o)?"":String(o);Ui(a,c)&&(a.value=c)}else a[n]=o}}}function Ui(t,e){return!t.composing&&("OPTION"===t.tagName||zi(t,e)||qi(t,e))}function zi(t,e){var n=!0;try{n=document.activeElement!==t}catch(t){}return n&&t.value!==e}function qi(t,e){var n=t.value,r=t._vModifiers;if(i(r)){if(r.lazy)return!1;if(r.number)return d(n)!==d(e);if(r.trim)return n.trim()!==e.trim()}return n!==e}var Ni={create:Di,update:Di},Fi=b(function(t){var e={},n=/;(?![^(]*\))/g,r=/:(.+)/;return t.split(n).forEach(function(t){if(t){var n=t.split(r);n.length>1&&(e[n[0].trim()]=n[1].trim())}}),e});function Hi(t){var e=Wi(t.style);return t.staticStyle?$(t.staticStyle,e):e}function Wi(t){return Array.isArray(t)?T(t):"string"===typeof t?Fi(t):t}function Bi(t,e){var n,r={};if(e){var i=t;while(i.componentInstance)i=i.componentInstance._vnode,i&&i.data&&(n=Hi(i.data))&&$(r,n)}(n=Hi(t.data))&&$(r,n);var o=t;while(o=o.parent)o.data&&(n=Hi(o.data))&&$(r,n);return r}var Ki,Vi=/^--/,Zi=/\s*!important$/,Xi=function(t,e,n){if(Vi.test(e))t.style.setProperty(e,n);else if(Zi.test(n))t.style.setProperty(e,n.replace(Zi,""),"important");else{var r=Ji(e);if(Array.isArray(n))for(var i=0,o=n.length;i-1?e.split(/\s+/).forEach(function(e){return t.classList.add(e)}):t.classList.add(e);else{var n=" "+(t.getAttribute("class")||"")+" ";n.indexOf(" "+e+" ")<0&&t.setAttribute("class",(n+e).trim())}}function eo(t,e){if(e&&(e=e.trim()))if(t.classList)e.indexOf(" ")>-1?e.split(/\s+/).forEach(function(e){return t.classList.remove(e)}):t.classList.remove(e),t.classList.length||t.removeAttribute("class");else{var n=" "+(t.getAttribute("class")||"")+" ",r=" "+e+" ";while(n.indexOf(r)>=0)n=n.replace(r," ");n=n.trim(),n?t.setAttribute("class",n):t.removeAttribute("class")}}function no(t){if(t){if("object"===typeof t){var e={};return!1!==t.css&&$(e,ro(t.name||"v")),$(e,t),e}return"string"===typeof t?ro(t):void 0}}var ro=b(function(t){return{enterClass:t+"-enter",enterToClass:t+"-enter-to",enterActiveClass:t+"-enter-active",leaveClass:t+"-leave",leaveToClass:t+"-leave-to",leaveActiveClass:t+"-leave-active"}}),io=Z&&!Q,oo="transition",ao="animation",uo="transition",so="transitionend",co="animation",lo="animationend";io&&(void 0===window.ontransitionend&&void 0!==window.onwebkittransitionend&&(uo="WebkitTransition",so="webkitTransitionEnd"),void 0===window.onanimationend&&void 0!==window.onwebkitanimationend&&(co="WebkitAnimation",lo="webkitAnimationEnd"));var fo=Z?window.requestAnimationFrame?window.requestAnimationFrame.bind(window):setTimeout:function(t){return t()};function po(t){fo(function(){fo(t)})}function ho(t,e){var n=t._transitionClasses||(t._transitionClasses=[]);n.indexOf(e)<0&&(n.push(e),to(t,e))}function vo(t,e){t._transitionClasses&&g(t._transitionClasses,e),eo(t,e)}function yo(t,e,n){var r=_o(t,e),i=r.type,o=r.timeout,a=r.propCount;if(!i)return n();var u=i===oo?so:lo,s=0,c=function(){t.removeEventListener(u,l),n()},l=function(e){e.target===t&&++s>=a&&c()};setTimeout(function(){s0&&(n=oo,l=a,f=o.length):e===ao?c>0&&(n=ao,l=c,f=s.length):(l=Math.max(a,c),n=l>0?a>c?oo:ao:null,f=n?n===oo?o.length:s.length:0);var p=n===oo&&go.test(r[uo+"Property"]);return{type:n,timeout:l,propCount:f,hasTransform:p}}function mo(t,e){while(t.length1}function Oo(t,e){!0!==e.data.show&&wo(e)}var Co=Z?{create:Oo,activate:Oo,remove:function(t,e){!0!==t.data.show?xo(t,e):e()}}:{},Eo=[Oi,ji,Mi,Ni,Qi,Co],jo=Eo.concat(wi),So=hi({nodeOps:oi,modules:jo});Q&&document.addEventListener("selectionchange",function(){var t=document.activeElement;t&&t.vmodel&&Do(t,"input")});var $o={inserted:function(t,e,n,r){"select"===n.tag?(r.elm&&!r.elm._vOptions?_e(n,"postpatch",function(){$o.componentUpdated(t,e,n)}):To(t,e,n.context),t._vOptions=[].map.call(t.options,Io)):("textarea"===n.tag||Br(t.type))&&(t._vModifiers=e.modifiers,e.modifiers.lazy||(t.addEventListener("compositionstart",Po),t.addEventListener("compositionend",Mo),t.addEventListener("change",Mo),Q&&(t.vmodel=!0)))},componentUpdated:function(t,e,n){if("select"===n.tag){To(t,e,n.context);var r=t._vOptions,i=t._vOptions=[].map.call(t.options,Io);if(i.some(function(t,e){return!P(t,r[e])})){var o=t.multiple?e.value.some(function(t){return Lo(t,i)}):e.value!==e.oldValue&&Lo(e.value,i);o&&Do(t,"change")}}}};function To(t,e,n){Ro(t,e,n),(Y||tt)&&setTimeout(function(){Ro(t,e,n)},0)}function Ro(t,e,n){var r=e.value,i=t.multiple;if(!i||Array.isArray(r)){for(var o,a,u=0,s=t.options.length;u-1,a.selected!==o&&(a.selected=o);else if(P(Io(a),r))return void(t.selectedIndex!==u&&(t.selectedIndex=u));i||(t.selectedIndex=-1)}}function Lo(t,e){return e.every(function(e){return!P(e,t)})}function Io(t){return"_value"in t?t._value:t.value}function Po(t){t.target.composing=!0}function Mo(t){t.target.composing&&(t.target.composing=!1,Do(t.target,"input"))}function Do(t,e){var n=document.createEvent("HTMLEvents");n.initEvent(e,!0,!0),t.dispatchEvent(n)}function Uo(t){return!t.componentInstance||t.data&&t.data.transition?t:Uo(t.componentInstance._vnode)}var zo={bind:function(t,e,n){var r=e.value;n=Uo(n);var i=n.data&&n.data.transition,o=t.__vOriginalDisplay="none"===t.style.display?"":t.style.display;r&&i?(n.data.show=!0,wo(n,function(){t.style.display=o})):t.style.display=r?o:"none"},update:function(t,e,n){var r=e.value,i=e.oldValue;if(!r!==!i){n=Uo(n);var o=n.data&&n.data.transition;o?(n.data.show=!0,r?wo(n,function(){t.style.display=t.__vOriginalDisplay}):xo(n,function(){t.style.display="none"})):t.style.display=r?t.__vOriginalDisplay:"none"}},unbind:function(t,e,n,r,i){i||(t.style.display=t.__vOriginalDisplay)}},qo={model:$o,show:zo},No={name:String,appear:Boolean,css:Boolean,mode:String,type:String,enterClass:String,leaveClass:String,enterToClass:String,leaveToClass:String,enterActiveClass:String,leaveActiveClass:String,appearClass:String,appearActiveClass:String,appearToClass:String,duration:[Number,String,Object]};function Fo(t){var e=t&&t.componentOptions;return e&&e.Ctor.options.abstract?Fo(Se(e.children)):t}function Ho(t){var e={},n=t.$options;for(var r in n.propsData)e[r]=t[r];var i=n._parentListeners;for(var o in i)e[x(o)]=i[o];return e}function Wo(t,e){if(/\d-keep-alive$/.test(e.tag))return t("keep-alive",{props:e.componentOptions.propsData})}function Bo(t){while(t=t.parent)if(t.data.transition)return!0}function Ko(t,e){return e.key===t.key&&e.tag===t.tag}var Vo={name:"transition",props:No,abstract:!0,render:function(t){var e=this,n=this.$slots.default;if(n&&(n=n.filter(function(t){return t.tag||je(t)}),n.length)){0;var r=this.mode;0;var i=n[0];if(Bo(this.$vnode))return i;var o=Fo(i);if(!o)return i;if(this._leaving)return Wo(t,i);var a="__transition-"+this._uid+"-";o.key=null==o.key?o.isComment?a+"comment":a+o.tag:u(o.key)?0===String(o.key).indexOf(a)?o.key:a+o.key:o.key;var s=(o.data||(o.data={})).transition=Ho(this),c=this._vnode,l=Fo(c);if(o.data.directives&&o.data.directives.some(function(t){return"show"===t.name})&&(o.data.show=!0),l&&l.data&&!Ko(o,l)&&!je(l)&&(!l.componentInstance||!l.componentInstance._vnode.isComment)){var f=l.data.transition=$({},s);if("out-in"===r)return this._leaving=!0,_e(f,"afterLeave",function(){e._leaving=!1,e.$forceUpdate()}),Wo(t,i);if("in-out"===r){if(je(o))return c;var p,h=function(){p()};_e(s,"afterEnter",h),_e(s,"enterCancelled",h),_e(f,"delayLeave",function(t){p=t})}}return i}}},Zo=$({tag:String,moveClass:String},No);delete Zo.mode;var Xo={props:Zo,render:function(t){for(var e=this.tag||this.$vnode.data.tag||"span",n=Object.create(null),r=this.prevChildren=this.children,i=this.$slots.default||[],o=this.children=[],a=Ho(this),u=0;u=2)t.mixin({beforeCreate:r});else{var n=t.prototype._init;t.prototype._init=function(t){void 0===t&&(t={}),t.init=t.init?[r].concat(t.init):r,n.call(this,t)}}function r(){var t=this.$options;t.store?this.$store="function"===typeof t.store?t.store():t.store:t.parent&&t.parent.$store&&(this.$store=t.parent.$store)}},i="undefined"!==typeof window&&window.__VUE_DEVTOOLS_GLOBAL_HOOK__;function o(t){i&&(t._devtoolHook=i,i.emit("vuex:init",t),i.on("vuex:travel-to-state",function(e){t.replaceState(e)}),t.subscribe(function(t,e){i.emit("vuex:mutation",t,e)}))}function a(t,e){Object.keys(t).forEach(function(n){return e(t[n],n)})}function u(t){return null!==t&&"object"===typeof t}function s(t){return t&&"function"===typeof t.then}var c=function(t,e){this.runtime=e,this._children=Object.create(null),this._rawModule=t;var n=t.state;this.state=("function"===typeof n?n():n)||{}},l={namespaced:{configurable:!0}};l.namespaced.get=function(){return!!this._rawModule.namespaced},c.prototype.addChild=function(t,e){this._children[t]=e},c.prototype.removeChild=function(t){delete this._children[t]},c.prototype.getChild=function(t){return this._children[t]},c.prototype.update=function(t){this._rawModule.namespaced=t.namespaced,t.actions&&(this._rawModule.actions=t.actions),t.mutations&&(this._rawModule.mutations=t.mutations),t.getters&&(this._rawModule.getters=t.getters)},c.prototype.forEachChild=function(t){a(this._children,t)},c.prototype.forEachGetter=function(t){this._rawModule.getters&&a(this._rawModule.getters,t)},c.prototype.forEachAction=function(t){this._rawModule.actions&&a(this._rawModule.actions,t)},c.prototype.forEachMutation=function(t){this._rawModule.mutations&&a(this._rawModule.mutations,t)},Object.defineProperties(c.prototype,l);var f=function(t){this.register([],t,!1)};function p(t,e,n){if(e.update(n),n.modules)for(var r in n.modules){if(!e.getChild(r))return void 0;p(t.concat(r),e.getChild(r),n.modules[r])}}f.prototype.get=function(t){return t.reduce(function(t,e){return t.getChild(e)},this.root)},f.prototype.getNamespace=function(t){var e=this.root;return t.reduce(function(t,n){return e=e.getChild(n),t+(e.namespaced?n+"/":"")},"")},f.prototype.update=function(t){p([],this.root,t)},f.prototype.register=function(t,e,n){var r=this;void 0===n&&(n=!0);var i=new c(e,n);if(0===t.length)this.root=i;else{var o=this.get(t.slice(0,-1));o.addChild(t[t.length-1],i)}e.modules&&a(e.modules,function(e,i){r.register(t.concat(i),e,n)})},f.prototype.unregister=function(t){var e=this.get(t.slice(0,-1)),n=t[t.length-1];e.getChild(n).runtime&&e.removeChild(n)};var h;var d=function(t){var e=this;void 0===t&&(t={}),!h&&"undefined"!==typeof window&&window.Vue&&j(window.Vue);var n=t.plugins;void 0===n&&(n=[]);var r=t.strict;void 0===r&&(r=!1);var i=t.state;void 0===i&&(i={}),"function"===typeof i&&(i=i()||{}),this._committing=!1,this._actions=Object.create(null),this._actionSubscribers=[],this._mutations=Object.create(null),this._wrappedGetters=Object.create(null),this._modules=new f(t),this._modulesNamespaceMap=Object.create(null),this._subscribers=[],this._watcherVM=new h;var a=this,u=this,s=u.dispatch,c=u.commit;this.dispatch=function(t,e){return s.call(a,t,e)},this.commit=function(t,e,n){return c.call(a,t,e,n)},this.strict=r,m(this,i,[],this._modules.root),_(this,i),n.forEach(function(t){return t(e)}),h.config.devtools&&o(this)},v={state:{configurable:!0}};function y(t,e){return e.indexOf(t)<0&&e.push(t),function(){var n=e.indexOf(t);n>-1&&e.splice(n,1)}}function g(t,e){t._actions=Object.create(null),t._mutations=Object.create(null),t._wrappedGetters=Object.create(null),t._modulesNamespaceMap=Object.create(null);var n=t.state;m(t,n,[],t._modules.root,!0),_(t,n,e)}function _(t,e,n){var r=t._vm;t.getters={};var i=t._wrappedGetters,o={};a(i,function(e,n){o[n]=function(){return e(t)},Object.defineProperty(t.getters,n,{get:function(){return t._vm[n]},enumerable:!0})});var u=h.config.silent;h.config.silent=!0,t._vm=new h({data:{$$state:e},computed:o}),h.config.silent=u,t.strict&&O(t),r&&(n&&t._withCommit(function(){r._data.$$state=null}),h.nextTick(function(){return r.$destroy()}))}function m(t,e,n,r,i){var o=!n.length,a=t._modules.getNamespace(n);if(r.namespaced&&(t._modulesNamespaceMap[a]=r),!o&&!i){var u=C(e,n.slice(0,-1)),s=n[n.length-1];t._withCommit(function(){h.set(u,s,r.state)})}var c=r.context=b(t,a,n);r.forEachMutation(function(e,n){var r=a+n;x(t,r,e,c)}),r.forEachAction(function(e,n){var r=e.root?n:a+n,i=e.handler||e;k(t,r,i,c)}),r.forEachGetter(function(e,n){var r=a+n;A(t,r,e,c)}),r.forEachChild(function(r,o){m(t,e,n.concat(o),r,i)})}function b(t,e,n){var r=""===e,i={dispatch:r?t.dispatch:function(n,r,i){var o=E(n,r,i),a=o.payload,u=o.options,s=o.type;return u&&u.root||(s=e+s),t.dispatch(s,a)},commit:r?t.commit:function(n,r,i){var o=E(n,r,i),a=o.payload,u=o.options,s=o.type;u&&u.root||(s=e+s),t.commit(s,a,u)}};return Object.defineProperties(i,{getters:{get:r?function(){return t.getters}:function(){return w(t,e)}},state:{get:function(){return C(t.state,n)}}}),i}function w(t,e){var n={},r=e.length;return Object.keys(t.getters).forEach(function(i){if(i.slice(0,r)===e){var o=i.slice(r);Object.defineProperty(n,o,{get:function(){return t.getters[i]},enumerable:!0})}}),n}function x(t,e,n,r){var i=t._mutations[e]||(t._mutations[e]=[]);i.push(function(e){n.call(t,r.state,e)})}function k(t,e,n,r){var i=t._actions[e]||(t._actions[e]=[]);i.push(function(e,i){var o=n.call(t,{dispatch:r.dispatch,commit:r.commit,getters:r.getters,state:r.state,rootGetters:t.getters,rootState:t.state},e,i);return s(o)||(o=Promise.resolve(o)),t._devtoolHook?o.catch(function(e){throw t._devtoolHook.emit("vuex:error",e),e}):o})}function A(t,e,n,r){t._wrappedGetters[e]||(t._wrappedGetters[e]=function(t){return n(r.state,r.getters,t.state,t.getters)})}function O(t){t._vm.$watch(function(){return this._data.$$state},function(){0},{deep:!0,sync:!0})}function C(t,e){return e.length?e.reduce(function(t,e){return t[e]},t):t}function E(t,e,n){return u(t)&&t.type&&(n=e,e=t,t=t.type),{type:t,payload:e,options:n}}function j(t){h&&t===h||(h=t,r(h))}v.state.get=function(){return this._vm._data.$$state},v.state.set=function(t){0},d.prototype.commit=function(t,e,n){var r=this,i=E(t,e,n),o=i.type,a=i.payload,u=(i.options,{type:o,payload:a}),s=this._mutations[o];s&&(this._withCommit(function(){s.forEach(function(t){t(a)})}),this._subscribers.forEach(function(t){return t(u,r.state)}))},d.prototype.dispatch=function(t,e){var n=this,r=E(t,e),i=r.type,o=r.payload,a={type:i,payload:o},u=this._actions[i];if(u)return this._actionSubscribers.forEach(function(t){return t(a,n.state)}),u.length>1?Promise.all(u.map(function(t){return t(o)})):u[0](o)},d.prototype.subscribe=function(t){return y(t,this._subscribers)},d.prototype.subscribeAction=function(t){return y(t,this._actionSubscribers)},d.prototype.watch=function(t,e,n){var r=this;return this._watcherVM.$watch(function(){return t(r.state,r.getters)},e,n)},d.prototype.replaceState=function(t){var e=this;this._withCommit(function(){e._vm._data.$$state=t})},d.prototype.registerModule=function(t,e,n){void 0===n&&(n={}),"string"===typeof t&&(t=[t]),this._modules.register(t,e),m(this,this.state,t,this._modules.get(t),n.preserveState),_(this,this.state)},d.prototype.unregisterModule=function(t){var e=this;"string"===typeof t&&(t=[t]),this._modules.unregister(t),this._withCommit(function(){var n=C(e.state,t.slice(0,-1));h.delete(n,t[t.length-1])}),g(this)},d.prototype.hotUpdate=function(t){this._modules.update(t),g(this,!0)},d.prototype._withCommit=function(t){var e=this._committing;this._committing=!0,t(),this._committing=e},Object.defineProperties(d.prototype,v);var S=P(function(t,e){var n={};return I(e).forEach(function(e){var r=e.key,i=e.val;n[r]=function(){var e=this.$store.state,n=this.$store.getters;if(t){var r=M(this.$store,"mapState",t);if(!r)return;e=r.context.state,n=r.context.getters}return"function"===typeof i?i.call(this,e,n):e[i]},n[r].vuex=!0}),n}),$=P(function(t,e){var n={};return I(e).forEach(function(e){var r=e.key,i=e.val;n[r]=function(){var e=[],n=arguments.length;while(n--)e[n]=arguments[n];var r=this.$store.commit;if(t){var o=M(this.$store,"mapMutations",t);if(!o)return;r=o.context.commit}return"function"===typeof i?i.apply(this,[r].concat(e)):r.apply(this.$store,[i].concat(e))}}),n}),T=P(function(t,e){var n={};return I(e).forEach(function(e){var r=e.key,i=e.val;i=t+i,n[r]=function(){if(!t||M(this.$store,"mapGetters",t))return this.$store.getters[i]},n[r].vuex=!0}),n}),R=P(function(t,e){var n={};return I(e).forEach(function(e){var r=e.key,i=e.val;n[r]=function(){var e=[],n=arguments.length;while(n--)e[n]=arguments[n];var r=this.$store.dispatch;if(t){var o=M(this.$store,"mapActions",t);if(!o)return;r=o.context.dispatch}return"function"===typeof i?i.apply(this,[r].concat(e)):r.apply(this.$store,[i].concat(e))}}),n}),L=function(t){return{mapState:S.bind(null,t),mapGetters:T.bind(null,t),mapMutations:$.bind(null,t),mapActions:R.bind(null,t)}};function I(t){return Array.isArray(t)?t.map(function(t){return{key:t,val:t}}):Object.keys(t).map(function(e){return{key:e,val:t[e]}})}function P(t){return function(e,n){return"string"!==typeof e?(n=e,e=""):"/"!==e.charAt(e.length-1)&&(e+="/"),t(e,n)}}function M(t,e,n){var r=t._modulesNamespaceMap[n];return r}var D={Store:d,install:j,version:"3.0.1",mapState:S,mapMutations:$,mapGetters:T,mapActions:R,createNamespacedHelpers:L};e["a"]=D},L9s1:function(t,e,n){"use strict";var r=n("XKFU"),i=n("0sh+"),o="includes";r(r.P+r.F*n("UUeW")(o),"String",{includes:function(t){return!!~i(this,t,o).indexOf(t,arguments.length>1?arguments[1]:void 0)}})},LQAc:function(t,e){t.exports=!1},LZWt:function(t,e){var n={}.toString;t.exports=function(t){return n.call(t).slice(8,-1)}},LvDl:function(t,e,n){(function(t,r){var i; +/** + * @license + * Lodash + * Copyright JS Foundation and other contributors + * Released under MIT license + * Based on Underscore.js 1.8.3 + * Copyright Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors + */(function(){var o,a="4.17.10",u=200,s="Unsupported core-js use. Try https://npms.io/search?q=ponyfill.",c="Expected a function",l="__lodash_hash_undefined__",f=500,p="__lodash_placeholder__",h=1,d=2,v=4,y=1,g=2,_=1,m=2,b=4,w=8,x=16,k=32,A=64,O=128,C=256,E=512,j=30,S="...",$=800,T=16,R=1,L=2,I=3,P=1/0,M=9007199254740991,D=1.7976931348623157e308,U=NaN,z=4294967295,q=z-1,N=z>>>1,F=[["ary",O],["bind",_],["bindKey",m],["curry",w],["curryRight",x],["flip",E],["partial",k],["partialRight",A],["rearg",C]],H="[object Arguments]",W="[object Array]",B="[object AsyncFunction]",K="[object Boolean]",V="[object Date]",Z="[object DOMException]",X="[object Error]",G="[object Function]",J="[object GeneratorFunction]",Y="[object Map]",Q="[object Number]",tt="[object Null]",et="[object Object]",nt="[object Promise]",rt="[object Proxy]",it="[object RegExp]",ot="[object Set]",at="[object String]",ut="[object Symbol]",st="[object Undefined]",ct="[object WeakMap]",lt="[object WeakSet]",ft="[object ArrayBuffer]",pt="[object DataView]",ht="[object Float32Array]",dt="[object Float64Array]",vt="[object Int8Array]",yt="[object Int16Array]",gt="[object Int32Array]",_t="[object Uint8Array]",mt="[object Uint8ClampedArray]",bt="[object Uint16Array]",wt="[object Uint32Array]",xt=/\b__p \+= '';/g,kt=/\b(__p \+=) '' \+/g,At=/(__e\(.*?\)|\b__t\)) \+\n'';/g,Ot=/&(?:amp|lt|gt|quot|#39);/g,Ct=/[&<>"']/g,Et=RegExp(Ot.source),jt=RegExp(Ct.source),St=/<%-([\s\S]+?)%>/g,$t=/<%([\s\S]+?)%>/g,Tt=/<%=([\s\S]+?)%>/g,Rt=/\.|\[(?:[^[\]]*|(["'])(?:(?!\1)[^\\]|\\.)*?\1)\]/,Lt=/^\w*$/,It=/[^.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\\]|\\.)*?)\2)\]|(?=(?:\.|\[\])(?:\.|\[\]|$))/g,Pt=/[\\^$.*+?()[\]{}|]/g,Mt=RegExp(Pt.source),Dt=/^\s+|\s+$/g,Ut=/^\s+/,zt=/\s+$/,qt=/\{(?:\n\/\* \[wrapped with .+\] \*\/)?\n?/,Nt=/\{\n\/\* \[wrapped with (.+)\] \*/,Ft=/,? & /,Ht=/[^\x00-\x2f\x3a-\x40\x5b-\x60\x7b-\x7f]+/g,Wt=/\\(\\)?/g,Bt=/\$\{([^\\}]*(?:\\.[^\\}]*)*)\}/g,Kt=/\w*$/,Vt=/^[-+]0x[0-9a-f]+$/i,Zt=/^0b[01]+$/i,Xt=/^\[object .+?Constructor\]$/,Gt=/^0o[0-7]+$/i,Jt=/^(?:0|[1-9]\d*)$/,Yt=/[\xc0-\xd6\xd8-\xf6\xf8-\xff\u0100-\u017f]/g,Qt=/($^)/,te=/['\n\r\u2028\u2029\\]/g,ee="\\ud800-\\udfff",ne="\\u0300-\\u036f",re="\\ufe20-\\ufe2f",ie="\\u20d0-\\u20ff",oe=ne+re+ie,ae="\\u2700-\\u27bf",ue="a-z\\xdf-\\xf6\\xf8-\\xff",se="\\xac\\xb1\\xd7\\xf7",ce="\\x00-\\x2f\\x3a-\\x40\\x5b-\\x60\\x7b-\\xbf",le="\\u2000-\\u206f",fe=" \\t\\x0b\\f\\xa0\\ufeff\\n\\r\\u2028\\u2029\\u1680\\u180e\\u2000\\u2001\\u2002\\u2003\\u2004\\u2005\\u2006\\u2007\\u2008\\u2009\\u200a\\u202f\\u205f\\u3000",pe="A-Z\\xc0-\\xd6\\xd8-\\xde",he="\\ufe0e\\ufe0f",de=se+ce+le+fe,ve="['’]",ye="["+ee+"]",ge="["+de+"]",_e="["+oe+"]",me="\\d+",be="["+ae+"]",we="["+ue+"]",xe="[^"+ee+de+me+ae+ue+pe+"]",ke="\\ud83c[\\udffb-\\udfff]",Ae="(?:"+_e+"|"+ke+")",Oe="[^"+ee+"]",Ce="(?:\\ud83c[\\udde6-\\uddff]){2}",Ee="[\\ud800-\\udbff][\\udc00-\\udfff]",je="["+pe+"]",Se="\\u200d",$e="(?:"+we+"|"+xe+")",Te="(?:"+je+"|"+xe+")",Re="(?:"+ve+"(?:d|ll|m|re|s|t|ve))?",Le="(?:"+ve+"(?:D|LL|M|RE|S|T|VE))?",Ie=Ae+"?",Pe="["+he+"]?",Me="(?:"+Se+"(?:"+[Oe,Ce,Ee].join("|")+")"+Pe+Ie+")*",De="\\d*(?:1st|2nd|3rd|(?![123])\\dth)(?=\\b|[A-Z_])",Ue="\\d*(?:1ST|2ND|3RD|(?![123])\\dTH)(?=\\b|[a-z_])",ze=Pe+Ie+Me,qe="(?:"+[be,Ce,Ee].join("|")+")"+ze,Ne="(?:"+[Oe+_e+"?",_e,Ce,Ee,ye].join("|")+")",Fe=RegExp(ve,"g"),He=RegExp(_e,"g"),We=RegExp(ke+"(?="+ke+")|"+Ne+ze,"g"),Be=RegExp([je+"?"+we+"+"+Re+"(?="+[ge,je,"$"].join("|")+")",Te+"+"+Le+"(?="+[ge,je+$e,"$"].join("|")+")",je+"?"+$e+"+"+Re,je+"+"+Le,Ue,De,me,qe].join("|"),"g"),Ke=RegExp("["+Se+ee+oe+he+"]"),Ve=/[a-z][A-Z]|[A-Z]{2,}[a-z]|[0-9][a-zA-Z]|[a-zA-Z][0-9]|[^a-zA-Z0-9 ]/,Ze=["Array","Buffer","DataView","Date","Error","Float32Array","Float64Array","Function","Int8Array","Int16Array","Int32Array","Map","Math","Object","Promise","RegExp","Set","String","Symbol","TypeError","Uint8Array","Uint8ClampedArray","Uint16Array","Uint32Array","WeakMap","_","clearTimeout","isFinite","parseInt","setTimeout"],Xe=-1,Ge={};Ge[ht]=Ge[dt]=Ge[vt]=Ge[yt]=Ge[gt]=Ge[_t]=Ge[mt]=Ge[bt]=Ge[wt]=!0,Ge[H]=Ge[W]=Ge[ft]=Ge[K]=Ge[pt]=Ge[V]=Ge[X]=Ge[G]=Ge[Y]=Ge[Q]=Ge[et]=Ge[it]=Ge[ot]=Ge[at]=Ge[ct]=!1;var Je={};Je[H]=Je[W]=Je[ft]=Je[pt]=Je[K]=Je[V]=Je[ht]=Je[dt]=Je[vt]=Je[yt]=Je[gt]=Je[Y]=Je[Q]=Je[et]=Je[it]=Je[ot]=Je[at]=Je[ut]=Je[_t]=Je[mt]=Je[bt]=Je[wt]=!0,Je[X]=Je[G]=Je[ct]=!1;var Ye={"À":"A","Á":"A","Â":"A","Ã":"A","Ä":"A","Å":"A","à":"a","á":"a","â":"a","ã":"a","ä":"a","å":"a","Ç":"C","ç":"c","Ð":"D","ð":"d","È":"E","É":"E","Ê":"E","Ë":"E","è":"e","é":"e","ê":"e","ë":"e","Ì":"I","Í":"I","Î":"I","Ï":"I","ì":"i","í":"i","î":"i","ï":"i","Ñ":"N","ñ":"n","Ò":"O","Ó":"O","Ô":"O","Õ":"O","Ö":"O","Ø":"O","ò":"o","ó":"o","ô":"o","õ":"o","ö":"o","ø":"o","Ù":"U","Ú":"U","Û":"U","Ü":"U","ù":"u","ú":"u","û":"u","ü":"u","Ý":"Y","ý":"y","ÿ":"y","Æ":"Ae","æ":"ae","Þ":"Th","þ":"th","ß":"ss","Ā":"A","Ă":"A","Ą":"A","ā":"a","ă":"a","ą":"a","Ć":"C","Ĉ":"C","Ċ":"C","Č":"C","ć":"c","ĉ":"c","ċ":"c","č":"c","Ď":"D","Đ":"D","ď":"d","đ":"d","Ē":"E","Ĕ":"E","Ė":"E","Ę":"E","Ě":"E","ē":"e","ĕ":"e","ė":"e","ę":"e","ě":"e","Ĝ":"G","Ğ":"G","Ġ":"G","Ģ":"G","ĝ":"g","ğ":"g","ġ":"g","ģ":"g","Ĥ":"H","Ħ":"H","ĥ":"h","ħ":"h","Ĩ":"I","Ī":"I","Ĭ":"I","Į":"I","İ":"I","ĩ":"i","ī":"i","ĭ":"i","į":"i","ı":"i","Ĵ":"J","ĵ":"j","Ķ":"K","ķ":"k","ĸ":"k","Ĺ":"L","Ļ":"L","Ľ":"L","Ŀ":"L","Ł":"L","ĺ":"l","ļ":"l","ľ":"l","ŀ":"l","ł":"l","Ń":"N","Ņ":"N","Ň":"N","Ŋ":"N","ń":"n","ņ":"n","ň":"n","ŋ":"n","Ō":"O","Ŏ":"O","Ő":"O","ō":"o","ŏ":"o","ő":"o","Ŕ":"R","Ŗ":"R","Ř":"R","ŕ":"r","ŗ":"r","ř":"r","Ś":"S","Ŝ":"S","Ş":"S","Š":"S","ś":"s","ŝ":"s","ş":"s","š":"s","Ţ":"T","Ť":"T","Ŧ":"T","ţ":"t","ť":"t","ŧ":"t","Ũ":"U","Ū":"U","Ŭ":"U","Ů":"U","Ű":"U","Ų":"U","ũ":"u","ū":"u","ŭ":"u","ů":"u","ű":"u","ų":"u","Ŵ":"W","ŵ":"w","Ŷ":"Y","ŷ":"y","Ÿ":"Y","Ź":"Z","Ż":"Z","Ž":"Z","ź":"z","ż":"z","ž":"z","IJ":"IJ","ij":"ij","Œ":"Oe","œ":"oe","ʼn":"'n","ſ":"s"},Qe={"&":"&","<":"<",">":">",'"':""","'":"'"},tn={"&":"&","<":"<",">":">",""":'"',"'":"'"},en={"\\":"\\","'":"'","\n":"n","\r":"r","\u2028":"u2028","\u2029":"u2029"},nn=parseFloat,rn=parseInt,on="object"==typeof t&&t&&t.Object===Object&&t,an="object"==typeof self&&self&&self.Object===Object&&self,un=on||an||Function("return this")(),sn="object"==typeof e&&e&&!e.nodeType&&e,cn=sn&&"object"==typeof r&&r&&!r.nodeType&&r,ln=cn&&cn.exports===sn,fn=ln&&on.process,pn=function(){try{var t=cn&&cn.require&&cn.require("util").types;return t||fn&&fn.binding&&fn.binding("util")}catch(t){}}(),hn=pn&&pn.isArrayBuffer,dn=pn&&pn.isDate,vn=pn&&pn.isMap,yn=pn&&pn.isRegExp,gn=pn&&pn.isSet,_n=pn&&pn.isTypedArray;function mn(t,e,n){switch(n.length){case 0:return t.call(e);case 1:return t.call(e,n[0]);case 2:return t.call(e,n[0],n[1]);case 3:return t.call(e,n[0],n[1],n[2])}return t.apply(e,n)}function bn(t,e,n,r){var i=-1,o=null==t?0:t.length;while(++i-1}function Cn(t,e,n){var r=-1,i=null==t?0:t.length;while(++r-1);return n}function Yn(t,e){var n=t.length;while(n--&&Dn(e,t[n],0)>-1);return n}function Qn(t,e){var n=t.length,r=0;while(n--)t[n]===e&&++r;return r}var tr=Fn(Ye),er=Fn(Qe);function nr(t){return"\\"+en[t]}function rr(t,e){return null==t?o:t[e]}function ir(t){return Ke.test(t)}function or(t){return Ve.test(t)}function ar(t){var e,n=[];while(!(e=t.next()).done)n.push(e.value);return n}function ur(t){var e=-1,n=Array(t.size);return t.forEach(function(t,r){n[++e]=[r,t]}),n}function sr(t,e){return function(n){return t(e(n))}}function cr(t,e){var n=-1,r=t.length,i=0,o=[];while(++n-1}function Nr(t,e){var n=this.__data__,r=ci(n,t);return r<0?(++this.size,n.push([t,e])):n[r][1]=e,this}function Fr(t){var e=-1,n=null==t?0:t.length;this.clear();while(++e=e?t:e)),t}function yi(t,e,n,r,i,a){var u,s=e&h,c=e&d,l=e&v;if(n&&(u=i?n(t,r,i,a):n(t)),u!==o)return u;if(!xl(t))return t;var f=al(t);if(f){if(u=tu(t),!s)return ra(t,u)}else{var p=Ga(t),y=p==G||p==J;if(fl(t))return Vo(t,s);if(p==et||p==H||y&&!i){if(u=c||y?{}:eu(t),!s)return c?aa(t,pi(u,t)):oa(t,fi(u,t))}else{if(!Je[p])return i?t:{};u=nu(t,p,s)}}a||(a=new Jr);var g=a.get(t);if(g)return g;if(a.set(t,u),Pl(t))return t.forEach(function(r){u.add(yi(r,e,n,r,t,a))}),u;if(Al(t))return t.forEach(function(r,i){u.set(i,yi(r,e,n,i,t,a))}),u;var _=l?c?za:Ua:c?xf:wf,m=f?o:_(t);return wn(m||t,function(r,i){m&&(i=r,r=t[i]),si(u,i,yi(r,e,n,i,t,a))}),u}function gi(t){var e=wf(t);return function(n){return _i(n,t,e)}}function _i(t,e,n){var r=n.length;if(null==t)return!r;t=ne(t);while(r--){var i=n[r],a=e[i],u=t[i];if(u===o&&!(i in t)||!a(u))return!1}return!0}function mi(t,e,n){if("function"!=typeof t)throw new oe(c);return ku(function(){t.apply(o,n)},e)}function bi(t,e,n,r){var i=-1,o=On,a=!0,s=t.length,c=[],l=e.length;if(!s)return c;n&&(e=En(e,Zn(n))),r?(o=Cn,a=!1):e.length>=u&&(o=Gn,a=!1,e=new Zr(e));t:while(++ii?0:i+n),r=r===o||r>i?i:Kl(r),r<0&&(r+=i),r=n>r?0:Vl(r);while(n0&&n(u)?e>1?Ei(u,e-1,n,r,i):jn(i,u):r||(i[i.length]=u)}return i}var ji=la(),Si=la(!0);function $i(t,e){return t&&ji(t,e,wf)}function Ti(t,e){return t&&Si(t,e,wf)}function Ri(t,e){return An(e,function(e){return ml(t[e])})}function Li(t,e){e=Ho(e,t);var n=0,r=e.length;while(null!=t&&ne}function Di(t,e){return null!=t&&fe.call(t,e)}function Ui(t,e){return null!=t&&e in ne(t)}function zi(t,e,n){return t>=Ne(e,n)&&t=120&&p.length>=120)?new Zr(s&&p):o}p=t[0];var h=-1,d=c[0];t:while(++h-1)u!==t&&Oe.call(u,s,1),Oe.call(t,s,1)}return t}function yo(t,e){var n=t?e.length:0,r=n-1;while(n--){var i=e[n];if(n==r||i!==o){var o=i;ou(i)?Oe.call(t,i,1):Po(t,i)}}return t}function go(t,e){return t+Ie(Ke()*(e-t+1))}function _o(t,e,r,i){var o=-1,a=qe(Le((e-t)/(r||1)),0),u=n(a);while(a--)u[i?a:++o]=t,t+=r;return u}function mo(t,e){var n="";if(!t||e<1||e>M)return n;do{e%2&&(n+=t),e=Ie(e/2),e&&(t+=t)}while(e);return n}function bo(t,e){return Au(mu(t,e,Sp),t+"")}function wo(t){return ii(qf(t))}function xo(t,e){var n=qf(t);return Eu(n,vi(e,0,n.length))}function ko(t,e,n,r){if(!xl(t))return t;e=Ho(e,t);var i=-1,a=e.length,u=a-1,s=t;while(null!=s&&++io?0:o+e),r=r>o?o:r,r<0&&(r+=o),o=e>r?0:r-e>>>0,e>>>=0;var a=n(o);while(++i>>1,a=t[o];null!==a&&!Dl(a)&&(n?a<=e:a=u){var l=e?null:ja(t);if(l)return fr(l);a=!1,i=Gn,c=new Zr}else c=e?[]:s;t:while(++r=r?t:Eo(t,e,n)}var Ko=$e||function(t){return un.clearTimeout(t)};function Vo(t,e){if(e)return t.slice();var n=t.length,r=we?we(n):new t.constructor(n);return t.copy(r),r}function Zo(t){var e=new t.constructor(t.byteLength);return new be(e).set(new be(t)),e}function Xo(t,e){var n=e?Zo(t.buffer):t.buffer;return new t.constructor(n,t.byteOffset,t.byteLength)}function Go(t){var e=new t.constructor(t.source,Kt.exec(t));return e.lastIndex=t.lastIndex,e}function Jo(t){return _r?ne(_r.call(t)):{}}function Yo(t,e){var n=e?Zo(t.buffer):t.buffer;return new t.constructor(n,t.byteOffset,t.length)}function Qo(t,e){if(t!==e){var n=t!==o,r=null===t,i=t===t,a=Dl(t),u=e!==o,s=null===e,c=e===e,l=Dl(e);if(!s&&!l&&!a&&t>e||a&&u&&c&&!s&&!l||r&&u&&c||!n&&c||!i)return 1;if(!r&&!a&&!l&&t=u)return s;var c=n[r];return s*("desc"==c?-1:1)}}return t.index-e.index}function ea(t,e,r,i){var o=-1,a=t.length,u=r.length,s=-1,c=e.length,l=qe(a-u,0),f=n(c+l),p=!i;while(++s1?n[i-1]:o,u=i>2?n[2]:o;a=t.length>3&&"function"==typeof a?(i--,a):o,u&&au(n[0],n[1],u)&&(a=i<3?o:a,i=1),e=ne(e);while(++r-1?i[a?e[u]:u]:o}}function ga(t){return Da(function(e){var n=e.length,r=n,i=Or.prototype.thru;t&&e.reverse();while(r--){var a=e[r];if("function"!=typeof a)throw new oe(c);if(i&&!u&&"wrapper"==Na(a))var u=new Or([],!0)}r=u?r:n;while(++r1&&_.reverse(),p&&ls))return!1;var l=a.get(t);if(l&&a.get(e))return l==e;var f=-1,p=!0,h=n&g?new Zr:o;a.set(t,e),a.set(e,t);while(++f1?"& ":"")+e[r],e=e.join(n>2?", ":" "),t.replace(qt,"{\n/* [wrapped with "+e+"] */\n")}function iu(t){return al(t)||ol(t)||!!(Ce&&t&&t[Ce])}function ou(t,e){var n=typeof t;return e=null==e?M:e,!!e&&("number"==n||"symbol"!=n&&Jt.test(t))&&t>-1&&t%1==0&&t0){if(++e>=$)return arguments[0]}else e=0;return t.apply(o,arguments)}}function Eu(t,e){var n=-1,r=t.length,i=r-1;e=e===o?r:e;while(++n1?t[e-1]:o;return n="function"==typeof n?(t.pop(),n):o,Is(t,n)});function Hs(t){var e=wr(t);return e.__chain__=!0,e}function Ws(t,e){return e(t),t}function Bs(t,e){return e(t)}var Ks=Da(function(t){var e=t.length,n=e?t[0]:0,r=this.__wrapped__,i=function(e){return di(e,t)};return!(e>1||this.__actions__.length)&&r instanceof Cr&&ou(n)?(r=r.slice(n,+n+(e?1:0)),r.__actions__.push({func:Bs,args:[i],thisArg:o}),new Or(r,this.__chain__).thru(function(t){return e&&!t.length&&t.push(o),t})):this.thru(i)});function Vs(){return Hs(this)}function Zs(){return new Or(this.value(),this.__chain__)}function Xs(){this.__values__===o&&(this.__values__=Wl(this.value()));var t=this.__index__>=this.__values__.length,e=t?o:this.__values__[this.__index__++];return{done:t,value:e}}function Gs(){return this}function Js(t){var e,n=this;while(n instanceof Ar){var r=Ru(n);r.__index__=0,r.__values__=o,e?i.__wrapped__=r:e=r;var i=r;n=n.__wrapped__}return i.__wrapped__=t,e}function Ys(){var t=this.__wrapped__;if(t instanceof Cr){var e=t;return this.__actions__.length&&(e=new Cr(this)),e=e.reverse(),e.__actions__.push({func:Bs,args:[ps],thisArg:o}),new Or(e,this.__chain__)}return this.thru(ps)}function Qs(){return Uo(this.__wrapped__,this.__actions__)}var tc=ua(function(t,e,n){fe.call(t,n)?++t[n]:hi(t,n,1)});function ec(t,e,n){var r=al(t)?kn:ki;return n&&au(t,e,n)&&(e=o),r(t,Ha(e,3))}function nc(t,e){var n=al(t)?An:Ci;return n(t,Ha(e,3))}var rc=ya(Wu),ic=ya(Bu);function oc(t,e){return Ei(dc(t,e),1)}function ac(t,e){return Ei(dc(t,e),P)}function uc(t,e,n){return n=n===o?1:Kl(n),Ei(dc(t,e),n)}function sc(t,e){var n=al(t)?wn:wi;return n(t,Ha(e,3))}function cc(t,e){var n=al(t)?xn:xi;return n(t,Ha(e,3))}var lc=ua(function(t,e,n){fe.call(t,n)?t[n].push(e):hi(t,n,[e])});function fc(t,e,n,r){t=sl(t)?t:qf(t),n=n&&!r?Kl(n):0;var i=t.length;return n<0&&(n=qe(i+n,0)),Ml(t)?n<=i&&t.indexOf(e,n)>-1:!!i&&Dn(t,e,n)>-1}var pc=bo(function(t,e,r){var i=-1,o="function"==typeof e,a=sl(t)?n(t.length):[];return wi(t,function(t){a[++i]=o?mn(e,t,r):Fi(t,e,r)}),a}),hc=ua(function(t,e,n){hi(t,n,e)});function dc(t,e){var n=al(t)?En:io;return n(t,Ha(e,3))}function vc(t,e,n,r){return null==t?[]:(al(e)||(e=null==e?[]:[e]),n=r?o:n,al(n)||(n=null==n?[]:[n]),lo(t,e,n))}var yc=ua(function(t,e,n){t[n?0:1].push(e)},function(){return[[],[]]});function gc(t,e,n){var r=al(t)?Sn:Hn,i=arguments.length<3;return r(t,Ha(e,4),n,i,wi)}function _c(t,e,n){var r=al(t)?$n:Hn,i=arguments.length<3;return r(t,Ha(e,4),n,i,xi)}function mc(t,e){var n=al(t)?An:Ci;return n(t,zc(Ha(e,3)))}function bc(t){var e=al(t)?ii:wo;return e(t)}function wc(t,e,n){e=(n?au(t,e,n):e===o)?1:Kl(e);var r=al(t)?oi:xo;return r(t,e)}function xc(t){var e=al(t)?ai:Co;return e(t)}function kc(t){if(null==t)return 0;if(sl(t))return Ml(t)?vr(t):t.length;var e=Ga(t);return e==Y||e==ot?t.size:eo(t).length}function Ac(t,e,n){var r=al(t)?Tn:jo;return n&&au(t,e,n)&&(e=o),r(t,Ha(e,3))}var Oc=bo(function(t,e){if(null==t)return[];var n=e.length;return n>1&&au(t,e[0],e[1])?e=[]:n>2&&au(e[0],e[1],e[2])&&(e=[e[0]]),lo(t,Ei(e,1),[])}),Cc=Te||function(){return un.Date.now()};function Ec(t,e){if("function"!=typeof e)throw new oe(c);return t=Kl(t),function(){if(--t<1)return e.apply(this,arguments)}}function jc(t,e,n){return e=n?o:e,e=t&&null==e?t.length:e,$a(t,O,o,o,o,o,e)}function Sc(t,e){var n;if("function"!=typeof e)throw new oe(c);return t=Kl(t),function(){return--t>0&&(n=e.apply(this,arguments)),t<=1&&(e=o),n}}var $c=bo(function(t,e,n){var r=_;if(n.length){var i=cr(n,Fa($c));r|=k}return $a(t,r,e,n,i)}),Tc=bo(function(t,e,n){var r=_|m;if(n.length){var i=cr(n,Fa(Tc));r|=k}return $a(e,r,t,n,i)});function Rc(t,e,n){e=n?o:e;var r=$a(t,w,o,o,o,o,o,e);return r.placeholder=Rc.placeholder,r}function Lc(t,e,n){e=n?o:e;var r=$a(t,x,o,o,o,o,o,e);return r.placeholder=Lc.placeholder,r}function Ic(t,e,n){var r,i,a,u,s,l,f=0,p=!1,h=!1,d=!0;if("function"!=typeof t)throw new oe(c);function v(e){var n=r,a=i;return r=i=o,f=e,u=t.apply(a,n),u}function y(t){return f=t,s=ku(m,e),p?v(t):u}function g(t){var n=t-l,r=t-f,i=e-n;return h?Ne(i,a-r):i}function _(t){var n=t-l,r=t-f;return l===o||n>=e||n<0||h&&r>=a}function m(){var t=Cc();if(_(t))return b(t);s=ku(m,g(t))}function b(t){return s=o,d&&r?v(t):(r=i=o,u)}function w(){s!==o&&Ko(s),f=0,r=l=i=s=o}function x(){return s===o?u:b(Cc())}function k(){var t=Cc(),n=_(t);if(r=arguments,i=this,l=t,n){if(s===o)return y(l);if(h)return s=ku(m,e),v(l)}return s===o&&(s=ku(m,e)),u}return e=Zl(e)||0,xl(n)&&(p=!!n.leading,h="maxWait"in n,a=h?qe(Zl(n.maxWait)||0,e):a,d="trailing"in n?!!n.trailing:d),k.cancel=w,k.flush=x,k}var Pc=bo(function(t,e){return mi(t,1,e)}),Mc=bo(function(t,e,n){return mi(t,Zl(e)||0,n)});function Dc(t){return $a(t,E)}function Uc(t,e){if("function"!=typeof t||null!=e&&"function"!=typeof e)throw new oe(c);var n=function(){var r=arguments,i=e?e.apply(this,r):r[0],o=n.cache;if(o.has(i))return o.get(i);var a=t.apply(this,r);return n.cache=o.set(i,a)||o,a};return n.cache=new(Uc.Cache||Fr),n}function zc(t){if("function"!=typeof t)throw new oe(c);return function(){var e=arguments;switch(e.length){case 0:return!t.call(this);case 1:return!t.call(this,e[0]);case 2:return!t.call(this,e[0],e[1]);case 3:return!t.call(this,e[0],e[1],e[2])}return!t.apply(this,e)}}function qc(t){return Sc(2,t)}Uc.Cache=Fr;var Nc=Wo(function(t,e){e=1==e.length&&al(e[0])?En(e[0],Zn(Ha())):En(Ei(e,1),Zn(Ha()));var n=e.length;return bo(function(r){var i=-1,o=Ne(r.length,n);while(++i=e}),ol=Hi(function(){return arguments}())?Hi:function(t){return kl(t)&&fe.call(t,"callee")&&!Ae.call(t,"callee")},al=n.isArray,ul=hn?Zn(hn):Wi;function sl(t){return null!=t&&wl(t.length)&&!ml(t)}function cl(t){return kl(t)&&sl(t)}function ll(t){return!0===t||!1===t||kl(t)&&Pi(t)==K}var fl=Me||Vp,pl=dn?Zn(dn):Bi;function hl(t){return kl(t)&&1===t.nodeType&&!Rl(t)}function dl(t){if(null==t)return!0;if(sl(t)&&(al(t)||"string"==typeof t||"function"==typeof t.splice||fl(t)||Ul(t)||ol(t)))return!t.length;var e=Ga(t);if(e==Y||e==ot)return!t.size;if(pu(t))return!eo(t).length;for(var n in t)if(fe.call(t,n))return!1;return!0}function vl(t,e){return Ki(t,e)}function yl(t,e,n){n="function"==typeof n?n:o;var r=n?n(t,e):o;return r===o?Ki(t,e,o,n):!!r}function gl(t){if(!kl(t))return!1;var e=Pi(t);return e==X||e==Z||"string"==typeof t.message&&"string"==typeof t.name&&!Rl(t)}function _l(t){return"number"==typeof t&&De(t)}function ml(t){if(!xl(t))return!1;var e=Pi(t);return e==G||e==J||e==B||e==rt}function bl(t){return"number"==typeof t&&t==Kl(t)}function wl(t){return"number"==typeof t&&t>-1&&t%1==0&&t<=M}function xl(t){var e=typeof t;return null!=t&&("object"==e||"function"==e)}function kl(t){return null!=t&&"object"==typeof t}var Al=vn?Zn(vn):Zi;function Ol(t,e){return t===e||Xi(t,e,Ba(e))}function Cl(t,e,n){return n="function"==typeof n?n:o,Xi(t,e,Ba(e),n)}function El(t){return Tl(t)&&t!=+t}function jl(t){if(fu(t))throw new i(s);return Gi(t)}function Sl(t){return null===t}function $l(t){return null==t}function Tl(t){return"number"==typeof t||kl(t)&&Pi(t)==Q}function Rl(t){if(!kl(t)||Pi(t)!=et)return!1;var e=xe(t);if(null===e)return!0;var n=fe.call(e,"constructor")&&e.constructor;return"function"==typeof n&&n instanceof n&&le.call(n)==ve}var Ll=yn?Zn(yn):Ji;function Il(t){return bl(t)&&t>=-M&&t<=M}var Pl=gn?Zn(gn):Yi;function Ml(t){return"string"==typeof t||!al(t)&&kl(t)&&Pi(t)==at}function Dl(t){return"symbol"==typeof t||kl(t)&&Pi(t)==ut}var Ul=_n?Zn(_n):Qi;function zl(t){return t===o}function ql(t){return kl(t)&&Ga(t)==ct}function Nl(t){return kl(t)&&Pi(t)==lt}var Fl=Oa(ro),Hl=Oa(function(t,e){return t<=e});function Wl(t){if(!t)return[];if(sl(t))return Ml(t)?yr(t):ra(t);if(Ee&&t[Ee])return ar(t[Ee]());var e=Ga(t),n=e==Y?ur:e==ot?fr:qf;return n(t)}function Bl(t){if(!t)return 0===t?t:0;if(t=Zl(t),t===P||t===-P){var e=t<0?-1:1;return e*D}return t===t?t:0}function Kl(t){var e=Bl(t),n=e%1;return e===e?n?e-n:e:0}function Vl(t){return t?vi(Kl(t),0,z):0}function Zl(t){if("number"==typeof t)return t;if(Dl(t))return U;if(xl(t)){var e="function"==typeof t.valueOf?t.valueOf():t;t=xl(e)?e+"":e}if("string"!=typeof t)return 0===t?t:+t;t=t.replace(Dt,"");var n=Zt.test(t);return n||Gt.test(t)?rn(t.slice(2),n?2:8):Vt.test(t)?U:+t}function Xl(t){return ia(t,xf(t))}function Gl(t){return t?vi(Kl(t),-M,M):0===t?t:0}function Jl(t){return null==t?"":Lo(t)}var Yl=sa(function(t,e){if(pu(e)||sl(e))ia(e,wf(e),t);else for(var n in e)fe.call(e,n)&&si(t,n,e[n])}),Ql=sa(function(t,e){ia(e,xf(e),t)}),tf=sa(function(t,e,n,r){ia(e,xf(e),t,r)}),ef=sa(function(t,e,n,r){ia(e,wf(e),t,r)}),nf=Da(di);function rf(t,e){var n=kr(t);return null==e?n:fi(n,e)}var of=bo(function(t,e){t=ne(t);var n=-1,r=e.length,i=r>2?e[2]:o;i&&au(e[0],e[1],i)&&(r=1);while(++n1),e}),ia(t,za(t),n),r&&(n=yi(n,h|d|v,La));var i=e.length;while(i--)Po(n,e[i]);return n});function jf(t,e){return $f(t,zc(Ha(e)))}var Sf=Da(function(t,e){return null==t?{}:fo(t,e)});function $f(t,e){if(null==t)return{};var n=En(za(t),function(t){return[t]});return e=Ha(e),po(t,n,function(t,n){return e(t,n[0])})}function Tf(t,e,n){e=Ho(e,t);var r=-1,i=e.length;i||(i=1,t=o);while(++re){var r=t;t=e,e=r}if(n||t%1||e%1){var i=Ke();return Ne(t+i*(e-t+nn("1e-"+((i+"").length-1))),e)}return go(t,e)}var Bf=ha(function(t,e,n){return e=e.toLowerCase(),t+(n?Kf(e):e)});function Kf(t){return mp(Jl(t).toLowerCase())}function Vf(t){return t=Jl(t),t&&t.replace(Yt,tr).replace(He,"")}function Zf(t,e,n){t=Jl(t),e=Lo(e);var r=t.length;n=n===o?r:vi(Kl(n),0,r);var i=n;return n-=e.length,n>=0&&t.slice(n,i)==e}function Xf(t){return t=Jl(t),t&&jt.test(t)?t.replace(Ct,er):t}function Gf(t){return t=Jl(t),t&&Mt.test(t)?t.replace(Pt,"\\$&"):t}var Jf=ha(function(t,e,n){return t+(n?"-":"")+e.toLowerCase()}),Yf=ha(function(t,e,n){return t+(n?" ":"")+e.toLowerCase()}),Qf=pa("toLowerCase");function tp(t,e,n){t=Jl(t),e=Kl(e);var r=e?vr(t):0;if(!e||r>=e)return t;var i=(e-r)/2;return xa(Ie(i),n)+t+xa(Le(i),n)}function ep(t,e,n){t=Jl(t),e=Kl(e);var r=e?vr(t):0;return e&&r>>0,n?(t=Jl(t),t&&("string"==typeof e||null!=e&&!Ll(e))&&(e=Lo(e),!e&&ir(t))?Bo(yr(t),0,n):t.split(e,n)):[]}var sp=ha(function(t,e,n){return t+(n?" ":"")+mp(e)});function cp(t,e,n){return t=Jl(t),n=null==n?0:vi(Kl(n),0,t.length),e=Lo(e),t.slice(n,n+e.length)==e}function lp(t,e,n){var r=wr.templateSettings;n&&au(t,e,n)&&(e=o),t=Jl(t),e=tf({},e,r,Ta);var i,a,u=tf({},e.imports,r.imports,Ta),s=wf(u),c=Xn(u,s),l=0,f=e.interpolate||Qt,p="__p += '",h=re((e.escape||Qt).source+"|"+f.source+"|"+(f===Tt?Bt:Qt).source+"|"+(e.evaluate||Qt).source+"|$","g"),d="//# sourceURL="+("sourceURL"in e?e.sourceURL:"lodash.templateSources["+ ++Xe+"]")+"\n";t.replace(h,function(e,n,r,o,u,s){return r||(r=o),p+=t.slice(l,s).replace(te,nr),n&&(i=!0,p+="' +\n__e("+n+") +\n'"),u&&(a=!0,p+="';\n"+u+";\n__p += '"),r&&(p+="' +\n((__t = ("+r+")) == null ? '' : __t) +\n'"),l=s+e.length,e}),p+="';\n";var v=e.variable;v||(p="with (obj) {\n"+p+"\n}\n"),p=(a?p.replace(xt,""):p).replace(kt,"$1").replace(At,"$1;"),p="function("+(v||"obj")+") {\n"+(v?"":"obj || (obj = {});\n")+"var __t, __p = ''"+(i?", __e = _.escape":"")+(a?", __j = Array.prototype.join;\nfunction print() { __p += __j.call(arguments, '') }\n":";\n")+p+"return __p\n}";var y=wp(function(){return Ht(s,d+"return "+p).apply(o,c)});if(y.source=p,gl(y))throw y;return y}function fp(t){return Jl(t).toLowerCase()}function pp(t){return Jl(t).toUpperCase()}function hp(t,e,n){if(t=Jl(t),t&&(n||e===o))return t.replace(Dt,"");if(!t||!(e=Lo(e)))return t;var r=yr(t),i=yr(e),a=Jn(r,i),u=Yn(r,i)+1;return Bo(r,a,u).join("")}function dp(t,e,n){if(t=Jl(t),t&&(n||e===o))return t.replace(zt,"");if(!t||!(e=Lo(e)))return t;var r=yr(t),i=Yn(r,yr(e))+1;return Bo(r,0,i).join("")}function vp(t,e,n){if(t=Jl(t),t&&(n||e===o))return t.replace(Ut,"");if(!t||!(e=Lo(e)))return t;var r=yr(t),i=Jn(r,yr(e));return Bo(r,i).join("")}function yp(t,e){var n=j,r=S;if(xl(e)){var i="separator"in e?e.separator:i;n="length"in e?Kl(e.length):n,r="omission"in e?Lo(e.omission):r}t=Jl(t);var a=t.length;if(ir(t)){var u=yr(t);a=u.length}if(n>=a)return t;var s=n-vr(r);if(s<1)return r;var c=u?Bo(u,0,s).join(""):t.slice(0,s);if(i===o)return c+r;if(u&&(s+=c.length-s),Ll(i)){if(t.slice(s).search(i)){var l,f=c;i.global||(i=re(i.source,Jl(Kt.exec(i))+"g")),i.lastIndex=0;while(l=i.exec(f))var p=l.index;c=c.slice(0,p===o?s:p)}}else if(t.indexOf(Lo(i),s)!=s){var h=c.lastIndexOf(i);h>-1&&(c=c.slice(0,h))}return c+r}function gp(t){return t=Jl(t),t&&Et.test(t)?t.replace(Ot,gr):t}var _p=ha(function(t,e,n){return t+(n?" ":"")+e.toUpperCase()}),mp=pa("toUpperCase");function bp(t,e,n){return t=Jl(t),e=n?o:e,e===o?or(t)?br(t):In(t):t.match(e)||[]}var wp=bo(function(t,e){try{return mn(t,o,e)}catch(t){return gl(t)?t:new i(t)}}),xp=Da(function(t,e){return wn(e,function(e){e=Su(e),hi(t,e,$c(t[e],t))}),t});function kp(t){var e=null==t?0:t.length,n=Ha();return t=e?En(t,function(t){if("function"!=typeof t[1])throw new oe(c);return[n(t[0]),t[1]]}):[],bo(function(n){var r=-1;while(++rM)return[];var n=z,r=Ne(t,z);e=Ha(e),t-=z;var i=Kn(r,e);while(++n0||e<0)?new Cr(n):(t<0?n=n.takeRight(-t):t&&(n=n.drop(t)),e!==o&&(e=Kl(e),n=e<0?n.dropRight(-e):n.take(e-t)),n)},Cr.prototype.takeRightWhile=function(t){return this.reverse().takeWhile(t).reverse()},Cr.prototype.toArray=function(){return this.take(z)},$i(Cr.prototype,function(t,e){var n=/^(?:filter|find|map|reject)|While$/.test(e),r=/^(?:head|last)$/.test(e),i=wr[r?"take"+("last"==e?"Right":""):e],a=r||/^find/.test(e);i&&(wr.prototype[e]=function(){var e=this.__wrapped__,u=r?[1]:arguments,s=e instanceof Cr,c=u[0],l=s||al(e),f=function(t){var e=i.apply(wr,jn([t],u));return r&&p?e[0]:e};l&&n&&"function"==typeof c&&1!=c.length&&(s=l=!1);var p=this.__chain__,h=!!this.__actions__.length,d=a&&!p,v=s&&!h;if(!a&&l){e=v?e:new Cr(this);var y=t.apply(e,u);return y.__actions__.push({func:Bs,args:[f],thisArg:o}),new Or(y,p)}return d&&v?t.apply(this,u):(y=this.thru(f),d?r?y.value()[0]:y.value():y)})}),wn(["pop","push","shift","sort","splice","unshift"],function(t){var e=ae[t],n=/^(?:push|sort|unshift)$/.test(t)?"tap":"thru",r=/^(?:pop|shift)$/.test(t);wr.prototype[t]=function(){var t=arguments;if(r&&!this.__chain__){var i=this.value();return e.apply(al(i)?i:[],t)}return this[n](function(n){return e.apply(al(n)?n:[],t)})}}),$i(Cr.prototype,function(t,e){var n=wr[e];if(n){var r=n.name+"",i=cn[r]||(cn[r]=[]);i.push({name:e,func:n})}}),cn[_a(o,m).name]=[{name:"wrapper",func:o}],Cr.prototype.clone=Er,Cr.prototype.reverse=jr,Cr.prototype.value=Sr,wr.prototype.at=Ks,wr.prototype.chain=Vs,wr.prototype.commit=Zs,wr.prototype.next=Xs,wr.prototype.plant=Js,wr.prototype.reverse=Ys,wr.prototype.toJSON=wr.prototype.valueOf=wr.prototype.value=Qs,wr.prototype.first=wr.prototype.head,Ee&&(wr.prototype[Ee]=Gs),wr},xr=wr();un._=xr,i=function(){return xr}.call(e,n,e,r),i===o||(r.exports=i)}).call(this)}).call(this,n("yLpj"),n("YuTi")(t))},LyE8:function(t,e,n){"use strict";var r=n("eeVq");t.exports=function(t,e){return!!t&&r(function(){e?t.call(null,function(){},1):t.call(null)})}},M6Qj:function(t,e,n){var r=n("hPIQ"),i=n("K0xU")("iterator"),o=Array.prototype;t.exports=function(t){return void 0!==t&&(r.Array===t||o[i]===t)}},MfQN:function(t,e){t.exports=function(t,e,n){var r=void 0===n;switch(e.length){case 0:return r?t():t.call(n);case 1:return r?t(e[0]):t.call(n,e[0]);case 2:return r?t(e[0],e[1]):t.call(n,e[0],e[1]);case 3:return r?t(e[0],e[1],e[2]):t.call(n,e[0],e[1],e[2]);case 4:return r?t(e[0],e[1],e[2],e[3]):t.call(n,e[0],e[1],e[2],e[3])}return t.apply(n,e)}},Mukb:function(t,e,n){var r=n("hswa"),i=n("RjD/");t.exports=n("nh4g")?function(t,e,n){return r.f(t,e,i(1,n))}:function(t,e,n){return t[e]=n,t}},OEbY:function(t,e,n){n("nh4g")&&"g"!=/./g.flags&&n("hswa").f(RegExp.prototype,"flags",{configurable:!0,get:n("C/va")})},OP3Y:function(t,e,n){var r=n("aagx"),i=n("S/j/"),o=n("YTvA")("IE_PROTO"),a=Object.prototype;t.exports=Object.getPrototypeOf||function(t){return t=i(t),r(t,o)?t[o]:"function"==typeof t.constructor&&t instanceof t.constructor?t.constructor.prototype:t instanceof Object?a:null}},QaDb:function(t,e,n){"use strict";var r=n("Kuth"),i=n("RjD/"),o=n("fyDq"),a={};n("Mukb")(a,n("K0xU")("iterator"),function(){return this}),t.exports=function(t,e,n){t.prototype=r(a,{next:i(1,n)}),o(t,e+" Iterator")}},RYi7:function(t,e){var n=Math.ceil,r=Math.floor;t.exports=function(t){return isNaN(t=+t)?0:(t>0?r:n)(t)}},"RjD/":function(t,e){t.exports=function(t,e){return{enumerable:!(1&t),configurable:!(2&t),writable:!(4&t),value:e}}},"S/j/":function(t,e,n){var r=n("vhPU");t.exports=function(t){return Object(r(t))}},SlkY:function(t,e,n){var r=n("m0Pp"),i=n("H6hf"),o=n("M6Qj"),a=n("y3w9"),u=n("ne8i"),s=n("J+6e"),c={},l={};e=t.exports=function(t,e,n,f,p){var h,d,v,y,g=p?function(){return t}:s(t),_=r(n,f,e?2:1),m=0;if("function"!=typeof g)throw TypeError(t+" is not iterable!");if(o(g)){for(h=u(t.length);h>m;m++)if(y=e?_(a(d=t[m])[0],d[1]):_(t[m]),y===c||y===l)return y}else for(v=g.call(t);!(d=v.next()).done;)if(y=i(v,_,d.value,e),y===c||y===l)return y};e.BREAK=c,e.RETURN=l},TrxG:function(t,e,n){var r=n("hcir");function i(t){return t._query=[Date.now().toString()],t}t.exports=function(t,e){return t.set("X-Requested-With","XMLHttpRequest"),t.set("Expires","-1"),t.set("Cache-Control","no-cache,no-store,must-revalidate,max-age=-1,private"),(r||e)&&i(t),t}},UUeW:function(t,e,n){var r=n("K0xU")("match");t.exports=function(t){var e=/./;try{"/./"[t](e)}catch(n){try{return e[r]=!1,!"/./"[t](e)}catch(t){}}return!0}},UqcF:function(t,e){e.f={}.propertyIsEnumerable},VRzm:function(t,e,n){"use strict";var r,i,o,a,u=n("LQAc"),s=n("dyZX"),c=n("m0Pp"),l=n("I8a+"),f=n("XKFU"),p=n("0/R4"),h=n("2OiF"),d=n("9gX7"),v=n("SlkY"),y=n("69bn"),g=n("GZEu").set,_=n("gHnn")(),m=n("pbhE"),b=n("nICZ"),w=n("ol8x"),x=n("vKrd"),k="Promise",A=s.TypeError,O=s.process,C=O&&O.versions,E=C&&C.v8||"",j=s[k],S="process"==l(O),$=function(){},T=i=m.f,R=!!function(){try{var t=j.resolve(1),e=(t.constructor={})[n("K0xU")("species")]=function(t){t($,$)};return(S||"function"==typeof PromiseRejectionEvent)&&t.then($)instanceof e&&0!==E.indexOf("6.6")&&-1===w.indexOf("Chrome/66")}catch(t){}}(),L=function(t){var e;return!(!p(t)||"function"!=typeof(e=t.then))&&e},I=function(t,e){if(!t._n){t._n=!0;var n=t._c;_(function(){var r=t._v,i=1==t._s,o=0,a=function(e){var n,o,a,u=i?e.ok:e.fail,s=e.resolve,c=e.reject,l=e.domain;try{u?(i||(2==t._h&&D(t),t._h=1),!0===u?n=r:(l&&l.enter(),n=u(r),l&&(l.exit(),a=!0)),n===e.promise?c(A("Promise-chain cycle")):(o=L(n))?o.call(n,s,c):s(n)):c(r)}catch(t){l&&!a&&l.exit(),c(t)}};while(n.length>o)a(n[o++]);t._c=[],t._n=!1,e&&!t._h&&P(t)})}},P=function(t){g.call(s,function(){var e,n,r,i=t._v,o=M(t);if(o&&(e=b(function(){S?O.emit("unhandledRejection",i,t):(n=s.onunhandledrejection)?n({promise:t,reason:i}):(r=s.console)&&r.error&&r.error("Unhandled promise rejection",i)}),t._h=S||M(t)?2:1),t._a=void 0,o&&e.e)throw e.v})},M=function(t){return 1!==t._h&&0===(t._a||t._c).length},D=function(t){g.call(s,function(){var e;S?O.emit("rejectionHandled",t):(e=s.onrejectionhandled)&&e({promise:t,reason:t._v})})},U=function(t){var e=this;e._d||(e._d=!0,e=e._w||e,e._v=t,e._s=2,e._a||(e._a=e._c.slice()),I(e,!0))},z=function(t){var e,n=this;if(!n._d){n._d=!0,n=n._w||n;try{if(n===t)throw A("Promise can't be resolved itself");(e=L(t))?_(function(){var r={_w:n,_d:!1};try{e.call(t,c(z,r,1),c(U,r,1))}catch(t){U.call(r,t)}}):(n._v=t,n._s=1,I(n,!1))}catch(t){U.call({_w:n,_d:!1},t)}}};R||(j=function(t){d(this,j,k,"_h"),h(t),r.call(this);try{t(c(z,this,1),c(U,this,1))}catch(t){U.call(this,t)}},r=function(t){this._c=[],this._a=void 0,this._s=0,this._d=!1,this._v=void 0,this._h=0,this._n=!1},r.prototype=n("3Lyj")(j.prototype,{then:function(t,e){var n=T(y(this,j));return n.ok="function"!=typeof t||t,n.fail="function"==typeof e&&e,n.domain=S?O.domain:void 0,this._c.push(n),this._a&&this._a.push(n),this._s&&I(this,!1),n.promise},catch:function(t){return this.then(void 0,t)}}),o=function(){var t=new r;this.promise=t,this.resolve=c(z,t,1),this.reject=c(U,t,1)},m.f=T=function(t){return t===j||t===a?new o(t):i(t)}),f(f.G+f.W+f.F*!R,{Promise:j}),n("fyDq")(j,k),n("elZq")(k),a=n("g3g5")[k],f(f.S+f.F*!R,k,{reject:function(t){var e=T(this),n=e.reject;return n(t),e.promise}}),f(f.S+f.F*(u||!R),k,{resolve:function(t){return x(u&&this===a?j:this,t)}}),f(f.S+f.F*!(R&&n("XMVh")(function(t){j.all(t)["catch"]($)})),k,{all:function(t){var e=this,n=T(e),r=n.resolve,i=n.reject,o=b(function(){var n=[],o=0,a=1;v(t,!1,function(t){var u=o++,s=!1;n.push(void 0),a++,e.resolve(t).then(function(t){s||(s=!0,n[u]=t,--a||r(n))},i)}),--a||r(n)});return o.e&&i(o.v),n.promise},race:function(t){var e=this,n=T(e),r=n.reject,i=b(function(){v(t,!1,function(t){e.resolve(t).then(n.resolve,r)})});return i.e&&r(i.v),n.promise}})},VTer:function(t,e,n){var r=n("g3g5"),i=n("dyZX"),o="__core-js_shared__",a=i[o]||(i[o]={});(t.exports=function(t,e){return a[t]||(a[t]=void 0!==e?e:{})})("versions",[]).push({version:r.version,mode:n("LQAc")?"pure":"global",copyright:"© 2018 Denis Pushkarev (zloirock.ru)"})},Vd3H:function(t,e,n){"use strict";var r=n("XKFU"),i=n("2OiF"),o=n("S/j/"),a=n("eeVq"),u=[].sort,s=[1,2,3];r(r.P+r.F*(a(function(){s.sort(void 0)})||!a(function(){s.sort(null)})||!n("LyE8")(u)),"Array",{sort:function(t){return void 0===t?u.call(o(this)):u.call(o(this),i(t))}})},XKFU:function(t,e,n){var r=n("dyZX"),i=n("g3g5"),o=n("Mukb"),a=n("KroJ"),u=n("m0Pp"),s="prototype",c=function(t,e,n){var l,f,p,h,d=t&c.F,v=t&c.G,y=t&c.S,g=t&c.P,_=t&c.B,m=v?r:y?r[e]||(r[e]={}):(r[e]||{})[s],b=v?i:i[e]||(i[e]={}),w=b[s]||(b[s]={});for(l in v&&(n=e),n)f=!d&&m&&void 0!==m[l],p=(f?m:n)[l],h=_&&f?u(p,r):g&&"function"==typeof p?u(Function.call,p):p,m&&a(m,l,p,t&c.U),b[l]!=p&&o(b,l,h),g&&w[l]!=p&&(w[l]=p)};r.core=i,c.F=1,c.G=2,c.S=4,c.P=8,c.B=16,c.W=32,c.U=64,c.R=128,t.exports=c},XMVh:function(t,e,n){var r=n("K0xU")("iterator"),i=!1;try{var o=[7][r]();o["return"]=function(){i=!0},Array.from(o,function(){throw 2})}catch(t){}t.exports=function(t,e){if(!e&&!i)return!1;var n=!1;try{var o=[7],a=o[r]();a.next=function(){return{done:n=!0}},o[r]=function(){return a},t(o)}catch(t){}return n}},YTvA:function(t,e,n){var r=n("VTer")("keys"),i=n("ylqs");t.exports=function(t){return r[t]||(r[t]=i(t))}},Ymqv:function(t,e,n){var r=n("LZWt");t.exports=Object("z").propertyIsEnumerable(0)?Object:function(t){return"String"==r(t)?t.split(""):Object(t)}},YuTi:function(t,e){t.exports=function(t){return t.webpackPolyfill||(t.deprecate=function(){},t.paths=[],t.children||(t.children=[]),Object.defineProperty(t,"loaded",{enumerable:!0,get:function(){return t.l}}),Object.defineProperty(t,"id",{enumerable:!0,get:function(){return t.i}}),t.webpackPolyfill=1),t}},Z2Ku:function(t,e,n){"use strict";var r=n("XKFU"),i=n("w2a5")(!0);r(r.P,"Array",{includes:function(t){return i(this,t,arguments.length>1?arguments[1]:void 0)}}),n("nGyu")("includes")},a1Th:function(t,e,n){"use strict";n("OEbY");var r=n("y3w9"),i=n("C/va"),o=n("nh4g"),a="toString",u=/./[a],s=function(t){n("KroJ")(RegExp.prototype,a,t,!0)};n("eeVq")(function(){return"/a/b"!=u.call({source:"a",flags:"b"})})?s(function(){var t=r(this);return"/".concat(t.source,"/","flags"in t?t.flags:!o&&t instanceof RegExp?i.call(t):void 0)}):u.name!=a&&s(function(){return u.call(this)})},aCFj:function(t,e,n){var r=n("Ymqv"),i=n("vhPU");t.exports=function(t){return r(i(t))}},aagx:function(t,e){var n={}.hasOwnProperty;t.exports=function(t,e){return n.call(t,e)}},apmT:function(t,e,n){var r=n("0/R4");t.exports=function(t,e){if(!r(t))return t;var n,i;if(e&&"function"==typeof(n=t.toString)&&!r(i=n.call(t)))return i;if("function"==typeof(n=t.valueOf)&&!r(i=n.call(t)))return i;if(!e&&"function"==typeof(n=t.toString)&&!r(i=n.call(t)))return i;throw TypeError("Can't convert object to primitive value")}},cpc2:function(t,e,n){function r(t){if(t)return i(t)}function i(t){for(var e in r.prototype)t[e]=r.prototype[e];return t}t.exports=r,r.prototype.on=r.prototype.addEventListener=function(t,e){return this._callbacks=this._callbacks||{},(this._callbacks["$"+t]=this._callbacks["$"+t]||[]).push(e),this},r.prototype.once=function(t,e){function n(){this.off(t,n),e.apply(this,arguments)}return n.fn=e,this.on(t,n),this},r.prototype.off=r.prototype.removeListener=r.prototype.removeAllListeners=r.prototype.removeEventListener=function(t,e){if(this._callbacks=this._callbacks||{},0==arguments.length)return this._callbacks={},this;var n,r=this._callbacks["$"+t];if(!r)return this;if(1==arguments.length)return delete this._callbacks["$"+t],this;for(var i=0;ic){var p,h=u(arguments[c++]),d=l?r(h).concat(l(h)):r(h),v=d.length,y=0;while(v>y)f.call(h,p=d[y++])&&(n[p]=h[p])}return n}:s},"d/Gc":function(t,e,n){var r=n("RYi7"),i=Math.max,o=Math.min;t.exports=function(t,e){return t=r(t),t<0?i(t+e,0):o(t,e)}},dRSK:function(t,e,n){"use strict";var r=n("XKFU"),i=n("CkkT")(5),o="find",a=!0;o in[]&&Array(1)[o](function(){a=!1}),r(r.P+r.F*a,"Array",{find:function(t){return i(this,t,arguments.length>1?arguments[1]:void 0)}}),n("nGyu")(o)},dyZX:function(t,e){var n=t.exports="undefined"!=typeof window&&window.Math==Math?window:"undefined"!=typeof self&&self.Math==Math?self:Function("return this")();"number"==typeof __g&&(__g=n)},eeVq:function(t,e){t.exports=function(t){try{return!!t()}catch(t){return!0}}},elZq:function(t,e,n){"use strict";var r=n("dyZX"),i=n("hswa"),o=n("nh4g"),a=n("K0xU")("species");t.exports=function(t){var e=r[t];o&&e&&!e[a]&&i.f(e,a,{configurable:!0,get:function(){return this}})}},"f3/d":function(t,e,n){var r=n("hswa").f,i=Function.prototype,o=/^\s*function ([^ (]*)/,a="name";a in i||n("nh4g")&&r(i,a,{configurable:!0,get:function(){try{return(""+this).match(o)[1]}catch(t){return""}}})},fyDq:function(t,e,n){var r=n("hswa").f,i=n("aagx"),o=n("K0xU")("toStringTag");t.exports=function(t,e,n){t&&!i(t=n?t:t.prototype,o)&&r(t,o,{configurable:!0,value:e})}},g3g5:function(t,e){var n=t.exports={version:"2.5.7"};"number"==typeof __e&&(__e=n)},gHnn:function(t,e,n){var r=n("dyZX"),i=n("GZEu").set,o=r.MutationObserver||r.WebKitMutationObserver,a=r.process,u=r.Promise,s="process"==n("LZWt")(a);t.exports=function(){var t,e,n,c=function(){var r,i;s&&(r=a.domain)&&r.exit();while(t){i=t.fn,t=t.next;try{i()}catch(r){throw t?n():e=void 0,r}}e=void 0,r&&r.enter()};if(s)n=function(){a.nextTick(c)};else if(!o||r.navigator&&r.navigator.standalone)if(u&&u.resolve){var l=u.resolve(void 0);n=function(){l.then(c)}}else n=function(){i.call(r,c)};else{var f=!0,p=document.createTextNode("");new o(c).observe(p,{characterData:!0}),n=function(){p.data=f=!f}}return function(r){var i={fn:r,next:void 0};e&&(e.next=i),t||(t=i,n()),e=i}}},hPIQ:function(t,e){t.exports={}},hcir:function(t,e){function n(){for(var t=3,e=document.createElement("b"),n=e.all||[];e.innerHTML="\x3c!--[if gt IE "+ ++t+"]>4?t:document.documentMode}t.exports=n()},hswa:function(t,e,n){var r=n("y3w9"),i=n("xpql"),o=n("apmT"),a=Object.defineProperty;e.f=n("nh4g")?Object.defineProperty:function(t,e,n){if(r(t),e=o(e,!0),r(n),i)try{return a(t,e,n)}catch(t){}if("get"in n||"set"in n)throw TypeError("Accessors not supported!");return"value"in n&&(t[e]=n.value),t}},iv4g:function(t,e,n){"use strict";function r(t){if(Array.isArray(t)){for(var e=0,n=new Array(t.length);e-1}var o={name:"router-view",functional:!0,props:{name:{type:String,default:"default"}},render:function(t,e){var n=e.props,r=e.children,i=e.parent,o=e.data;o.routerView=!0;var s=i.$createElement,c=n.name,l=i.$route,f=i._routerViewCache||(i._routerViewCache={}),p=0,h=!1;while(i&&i._routerRoot!==i)i.$vnode&&i.$vnode.data.routerView&&p++,i._inactive&&(h=!0),i=i.$parent;if(o.routerViewDepth=p,h)return s(f[c],o,r);var d=l.matched[p];if(!d)return f[c]=null,s();var v=f[c]=d.components[c];o.registerRouteInstance=function(t,e){var n=d.instances[c];(e&&n!==t||!e&&n===t)&&(d.instances[c]=e)},(o.hook||(o.hook={})).prepatch=function(t,e){d.instances[c]=e.componentInstance};var y=o.props=a(l,d.props&&d.props[c]);if(y){y=o.props=u({},y);var g=o.attrs=o.attrs||{};for(var _ in y)v.props&&_ in v.props||(g[_]=y[_],delete y[_])}return s(v,o,r)}};function a(t,e){switch(typeof e){case"undefined":return;case"object":return e;case"function":return e(t);case"boolean":return e?t.params:void 0;default:0}}function u(t,e){for(var n in e)t[n]=e[n];return t}var s=/[!'()*]/g,c=function(t){return"%"+t.charCodeAt(0).toString(16)},l=/%2C/g,f=function(t){return encodeURIComponent(t).replace(s,c).replace(l,",")},p=decodeURIComponent;function h(t,e,n){void 0===e&&(e={});var r,i=n||d;try{r=i(t||"")}catch(t){r={}}for(var o in e)r[o]=e[o];return r}function d(t){var e={};return t=t.trim().replace(/^(\?|#|&)/,""),t?(t.split("&").forEach(function(t){var n=t.replace(/\+/g," ").split("="),r=p(n.shift()),i=n.length>0?p(n.join("=")):null;void 0===e[r]?e[r]=i:Array.isArray(e[r])?e[r].push(i):e[r]=[e[r],i]}),e):e}function v(t){var e=t?Object.keys(t).map(function(e){var n=t[e];if(void 0===n)return"";if(null===n)return f(e);if(Array.isArray(n)){var r=[];return n.forEach(function(t){void 0!==t&&(null===t?r.push(f(e)):r.push(f(e)+"="+f(t)))}),r.join("&")}return f(e)+"="+f(n)}).filter(function(t){return t.length>0}).join("&"):null;return e?"?"+e:""}var y=/\/?$/;function g(t,e,n,r){var i=r&&r.options.stringifyQuery,o=e.query||{};try{o=_(o)}catch(t){}var a={name:e.name||t&&t.name,meta:t&&t.meta||{},path:e.path||"/",hash:e.hash||"",query:o,params:e.params||{},fullPath:w(e,i),matched:t?b(t):[]};return n&&(a.redirectedFrom=w(n,i)),Object.freeze(a)}function _(t){if(Array.isArray(t))return t.map(_);if(t&&"object"===typeof t){var e={};for(var n in t)e[n]=_(t[n]);return e}return t}var m=g(null,{path:"/"});function b(t){var e=[];while(t)e.unshift(t),t=t.parent;return e}function w(t,e){var n=t.path,r=t.query;void 0===r&&(r={});var i=t.hash;void 0===i&&(i="");var o=e||v;return(n||"/")+o(r)+i}function x(t,e){return e===m?t===e:!!e&&(t.path&&e.path?t.path.replace(y,"")===e.path.replace(y,"")&&t.hash===e.hash&&k(t.query,e.query):!(!t.name||!e.name)&&(t.name===e.name&&t.hash===e.hash&&k(t.query,e.query)&&k(t.params,e.params)))}function k(t,e){if(void 0===t&&(t={}),void 0===e&&(e={}),!t||!e)return t===e;var n=Object.keys(t),r=Object.keys(e);return n.length===r.length&&n.every(function(n){var r=t[n],i=e[n];return"object"===typeof r&&"object"===typeof i?k(r,i):String(r)===String(i)})}function A(t,e){return 0===t.path.replace(y,"/").indexOf(e.path.replace(y,"/"))&&(!e.hash||t.hash===e.hash)&&O(t.query,e.query)}function O(t,e){for(var n in e)if(!(n in t))return!1;return!0}var C,E=[String,Object],j=[String,Array],S={name:"router-link",props:{to:{type:E,required:!0},tag:{type:String,default:"a"},exact:Boolean,append:Boolean,replace:Boolean,activeClass:String,exactActiveClass:String,event:{type:j,default:"click"}},render:function(t){var e=this,n=this.$router,r=this.$route,i=n.resolve(this.to,r,this.append),o=i.location,a=i.route,u=i.href,s={},c=n.options.linkActiveClass,l=n.options.linkExactActiveClass,f=null==c?"router-link-active":c,p=null==l?"router-link-exact-active":l,h=null==this.activeClass?f:this.activeClass,d=null==this.exactActiveClass?p:this.exactActiveClass,v=o.path?g(null,o,null,n):a;s[d]=x(r,v),s[h]=this.exact?s[d]:A(r,v);var y=function(t){$(t)&&(e.replace?n.replace(o):n.push(o))},_={click:$};Array.isArray(this.event)?this.event.forEach(function(t){_[t]=y}):_[this.event]=y;var m={class:s};if("a"===this.tag)m.on=_,m.attrs={href:u};else{var b=T(this.$slots.default);if(b){b.isStatic=!1;var w=C.util.extend,k=b.data=w({},b.data);k.on=_;var O=b.data.attrs=w({},b.data.attrs);O.href=u}else m.on=_}return t(this.tag,m,this.$slots.default)}};function $(t){if(!(t.metaKey||t.altKey||t.ctrlKey||t.shiftKey)&&!t.defaultPrevented&&(void 0===t.button||0===t.button)){if(t.currentTarget&&t.currentTarget.getAttribute){var e=t.currentTarget.getAttribute("target");if(/\b_blank\b/i.test(e))return}return t.preventDefault&&t.preventDefault(),!0}}function T(t){if(t)for(var e,n=0;n=0&&(e=t.slice(r),t=t.slice(0,r));var i=t.indexOf("?");return i>=0&&(n=t.slice(i+1),t=t.slice(0,i)),{path:t,query:n,hash:e}}function M(t){return t.replace(/\/\//g,"/")}var D=Array.isArray||function(t){return"[object Array]"==Object.prototype.toString.call(t)},U=rt,z=W,q=B,N=Z,F=nt,H=new RegExp(["(\\\\.)","([\\/.])?(?:(?:\\:(\\w+)(?:\\(((?:\\\\.|[^\\\\()])+)\\))?|\\(((?:\\\\.|[^\\\\()])+)\\))([+*?])?|(\\*))"].join("|"),"g");function W(t,e){var n,r=[],i=0,o=0,a="",u=e&&e.delimiter||"/";while(null!=(n=H.exec(t))){var s=n[0],c=n[1],l=n.index;if(a+=t.slice(o,l),o=l+s.length,c)a+=c[1];else{var f=t[o],p=n[2],h=n[3],d=n[4],v=n[5],y=n[6],g=n[7];a&&(r.push(a),a="");var _=null!=p&&null!=f&&f!==p,m="+"===y||"*"===y,b="?"===y||"*"===y,w=n[2]||u,x=d||v;r.push({name:h||i++,prefix:p||"",delimiter:w,optional:b,repeat:m,partial:_,asterisk:!!g,pattern:x?G(x):g?".*":"[^"+X(w)+"]+?"})}}return o-1&&(u.params[p]=n.params[p]);if(c)return u.path=ot(c.path,u.params,'named route "'+s+'"'),l(c,u,a)}else if(u.path){u.params={};for(var h=0;h=t.length?n():t[i]?e(t[i],function(){r(i+1)}):r(i+1)};r(0)}function Pt(t){return function(e,n,r){var o=!1,a=0,u=null;Mt(t,function(t,e,n,s){if("function"===typeof t&&void 0===t.cid){o=!0,a++;var c,l=qt(function(e){zt(e)&&(e=e.default),t.resolved="function"===typeof e?e:C.extend(e),n.components[s]=e,a--,a<=0&&r()}),f=qt(function(t){var e="Failed to resolve async component "+s+": "+t;u||(u=i(t)?t:new Error(e),r(u))});try{c=t(l,f)}catch(t){f(t)}if(c)if("function"===typeof c.then)c.then(l,f);else{var p=c.component;p&&"function"===typeof p.then&&p.then(l,f)}}}),o||r()}}function Mt(t,e){return Dt(t.map(function(t){return Object.keys(t.components).map(function(n){return e(t.components[n],t.instances[n],t,n)})}))}function Dt(t){return Array.prototype.concat.apply([],t)}var Ut="function"===typeof Symbol&&"symbol"===typeof Symbol.toStringTag;function zt(t){return t.__esModule||Ut&&"Module"===t[Symbol.toStringTag]}function qt(t){var e=!1;return function(){var n=[],r=arguments.length;while(r--)n[r]=arguments[r];if(!e)return e=!0,t.apply(this,n)}}var Nt=function(t,e){this.router=t,this.base=Ft(e),this.current=m,this.pending=null,this.ready=!1,this.readyCbs=[],this.readyErrorCbs=[],this.errorCbs=[]};function Ft(t){if(!t)if(L){var e=document.querySelector("base");t=e&&e.getAttribute("href")||"/",t=t.replace(/^https?:\/\/[^\/]+/,"")}else t="/";return"/"!==t.charAt(0)&&(t="/"+t),t.replace(/\/$/,"")}function Ht(t,e){var n,r=Math.max(t.length,e.length);for(n=0;n=0?e.slice(0,n):e;return r+"#"+t}function oe(t){Ct?Rt(ie(t)):window.location.hash=t}function ae(t){Ct?Lt(ie(t)):window.location.replace(ie(t))}var ue=function(t){function e(e,n){t.call(this,e,n),this.stack=[],this.index=-1}return t&&(e.__proto__=t),e.prototype=Object.create(t&&t.prototype),e.prototype.constructor=e,e.prototype.push=function(t,e,n){var r=this;this.transitionTo(t,function(t){r.stack=r.stack.slice(0,r.index+1).concat(t),r.index++,e&&e(t)},n)},e.prototype.replace=function(t,e,n){var r=this;this.transitionTo(t,function(t){r.stack=r.stack.slice(0,r.index).concat(t),e&&e(t)},n)},e.prototype.go=function(t){var e=this,n=this.index+t;if(!(n<0||n>=this.stack.length)){var r=this.stack[n];this.confirmTransition(r,function(){e.index=n,e.updateRoute(r)})}},e.prototype.getCurrentLocation=function(){var t=this.stack[this.stack.length-1];return t?t.fullPath:"/"},e.prototype.ensureURL=function(){},e}(Nt),se=function(t){void 0===t&&(t={}),this.app=null,this.apps=[],this.options=t,this.beforeHooks=[],this.resolveHooks=[],this.afterHooks=[],this.matcher=pt(t.routes||[],this);var e=t.mode||"hash";switch(this.fallback="history"===e&&!Ct&&!1!==t.fallback,this.fallback&&(e="hash"),L||(e="abstract"),this.mode=e,e){case"history":this.history=new Yt(this,t.base);break;case"hash":this.history=new te(this,t.base,this.fallback);break;case"abstract":this.history=new ue(this,t.base);break;default:0}},ce={currentRoute:{configurable:!0}};function le(t,e){return t.push(e),function(){var n=t.indexOf(e);n>-1&&t.splice(n,1)}}function fe(t,e,n){var r="hash"===n?"#"+e:e;return t?M(t+"/"+r):r}se.prototype.match=function(t,e,n){return this.matcher.match(t,e,n)},ce.currentRoute.get=function(){return this.history&&this.history.current},se.prototype.init=function(t){var e=this;if(this.apps.push(t),!this.app){this.app=t;var n=this.history;if(n instanceof Yt)n.transitionTo(n.getCurrentLocation());else if(n instanceof te){var r=function(){n.setupListeners()};n.transitionTo(n.getCurrentLocation(),r,r)}n.listen(function(t){e.apps.forEach(function(e){e._route=t})})}},se.prototype.beforeEach=function(t){return le(this.beforeHooks,t)},se.prototype.beforeResolve=function(t){return le(this.resolveHooks,t)},se.prototype.afterEach=function(t){return le(this.afterHooks,t)},se.prototype.onReady=function(t,e){this.history.onReady(t,e)},se.prototype.onError=function(t){this.history.onError(t)},se.prototype.push=function(t,e,n){this.history.push(t,e,n)},se.prototype.replace=function(t,e,n){this.history.replace(t,e,n)},se.prototype.go=function(t){this.history.go(t)},se.prototype.back=function(){this.go(-1)},se.prototype.forward=function(){this.go(1)},se.prototype.getMatchedComponents=function(t){var e=t?t.matched?t:this.resolve(t).route:this.currentRoute;return e?[].concat.apply([],e.matched.map(function(t){return Object.keys(t.components).map(function(e){return t.components[e]})})):[]},se.prototype.resolve=function(t,e,n){var r=lt(t,e||this.history.current,n,this),i=this.match(r,e),o=i.redirectedFrom||i.fullPath,a=this.history.base,u=fe(a,o,this.mode);return{location:r,route:i,href:u,normalizedTo:r,resolved:i}},se.prototype.addRoutes=function(t){this.matcher.addRoutes(t),this.history.current!==m&&this.history.transitionTo(this.history.getCurrentLocation())},Object.defineProperties(se.prototype,ce),se.install=R,se.version="3.0.1",L&&window.Vue&&window.Vue.use(se),e["a"]=se},"k5N+":function(t,e,n){"use strict";function r(t){if(Array.isArray(t))return t}function i(t,e){var n=[],r=!0,i=!1,o=void 0;try{for(var a,u=t[Symbol.iterator]();!(r=(a=u.next()).done);r=!0)if(n.push(a.value),e&&n.length===e)break}catch(t){i=!0,o=t}finally{try{r||null==u["return"]||u["return"]()}finally{if(i)throw o}}return n}function o(){throw new TypeError("Invalid attempt to destructure non-iterable instance")}function a(t,e){return r(t)||i(t,e)||o()}n.d(e,"a",function(){return a})},kMlx:function(t,e,n){"use strict";var r=n("8zgK");function i(t){if(t)return o(t)}function o(t){for(var e in i.prototype)t[e]=i.prototype[e];return t}t.exports=i,i.prototype.clearTimeout=function(){return clearTimeout(this._timer),clearTimeout(this._responseTimeoutTimer),delete this._timer,delete this._responseTimeoutTimer,this},i.prototype.parse=function(t){return this._parser=t,this},i.prototype.responseType=function(t){return this._responseType=t,this},i.prototype.serialize=function(t){return this._serializer=t,this},i.prototype.timeout=function(t){if(!t||"object"!==typeof t)return this._timeout=t,this._responseTimeout=0,this;for(var e in t)switch(e){case"deadline":this._timeout=t.deadline;break;case"response":this._responseTimeout=t.response;break;default:console.warn("Unknown timeout option",e)}return this},i.prototype.retry=function(t,e){return 0!==arguments.length&&!0!==t||(t=1),t<=0&&(t=0),this._maxRetries=t,this._retries=0,this._retryCallback=e,this};var a=["ECONNRESET","ETIMEDOUT","EADDRINFO","ESOCKETTIMEDOUT"];i.prototype._shouldRetry=function(t,e){if(!this._maxRetries||this._retries++>=this._maxRetries)return!1;if(this._retryCallback)try{var n=this._retryCallback(t,e);if(!0===n)return!0;if(!1===n)return!1}catch(t){console.error(t)}if(e&&e.status&&e.status>=500&&501!=e.status)return!0;if(t){if(t.code&&~a.indexOf(t.code))return!0;if(t.timeout&&"ECONNABORTED"==t.code)return!0;if(t.crossDomain)return!0}return!1},i.prototype._retry=function(){return this.clearTimeout(),this.req&&(this.req=null,this.req=this.request()),this._aborted=!1,this.timedout=!1,this._end()},i.prototype.then=function(t,e){if(!this._fullfilledPromise){var n=this;this._endCalled&&console.warn("Warning: superagent request was sent twice, because both .end() and .then() were called. Never call .end() if you use promises"),this._fullfilledPromise=new Promise(function(t,e){n.end(function(n,r){n?e(n):t(r)})})}return this._fullfilledPromise.then(t,e)},i.prototype["catch"]=function(t){return this.then(void 0,t)},i.prototype.use=function(t){return t(this),this},i.prototype.ok=function(t){if("function"!==typeof t)throw Error("Callback required");return this._okCallback=t,this},i.prototype._isResponseOK=function(t){return!!t&&(this._okCallback?this._okCallback(t):t.status>=200&&t.status<300)},i.prototype.get=function(t){return this._header[t.toLowerCase()]},i.prototype.getHeader=i.prototype.get,i.prototype.set=function(t,e){if(r(t)){for(var n in t)this.set(n,t[n]);return this}return this._header[t.toLowerCase()]=e,this.header[t]=e,this},i.prototype.unset=function(t){return delete this._header[t.toLowerCase()],delete this.header[t],this},i.prototype.field=function(t,e){if(null===t||void 0===t)throw new Error(".field(name, val) name can not be empty");if(this._data&&console.error(".field() can't be used if .send() is used. Please use only .send() or only .field() & .attach()"),r(t)){for(var n in t)this.field(n,t[n]);return this}if(Array.isArray(e)){for(var i in e)this.field(t,e[i]);return this}if(null===e||void 0===e)throw new Error(".field(name, val) val can not be empty");return"boolean"===typeof e&&(e=""+e),this._getFormData().append(t,e),this},i.prototype.abort=function(){return this._aborted?this:(this._aborted=!0,this.xhr&&this.xhr.abort(),this.req&&this.req.abort(),this.clearTimeout(),this.emit("abort"),this)},i.prototype._auth=function(t,e,n,r){switch(n.type){case"basic":this.set("Authorization","Basic "+r(t+":"+e));break;case"auto":this.username=t,this.password=e;break;case"bearer":this.set("Authorization","Bearer "+t);break}return this},i.prototype.withCredentials=function(t){return void 0==t&&(t=!0),this._withCredentials=t,this},i.prototype.redirects=function(t){return this._maxRedirects=t,this},i.prototype.maxResponseSize=function(t){if("number"!==typeof t)throw TypeError("Invalid argument");return this._maxResponseSize=t,this},i.prototype.toJSON=function(){return{method:this.method,url:this.url,data:this._data,headers:this._header}},i.prototype.send=function(t){var e=r(t),n=this._header["content-type"];if(this._formData&&console.error(".send() can't be used if .attach() or .field() is used. Please use only .send() or only .field() & .attach()"),e&&!this._data)Array.isArray(t)?this._data=[]:this._isHost(t)||(this._data={});else if(t&&this._data&&this._isHost(this._data))throw Error("Can't merge these send calls");if(e&&r(this._data))for(var i in t)this._data[i]=t[i];else"string"==typeof t?(n||this.type("form"),n=this._header["content-type"],this._data="application/x-www-form-urlencoded"==n?this._data?this._data+"&"+t:t:(this._data||"")+t):this._data=t;return!e||this._isHost(t)?this:(n||this.type("json"),this)},i.prototype.sortQuery=function(t){return this._sort="undefined"===typeof t||t,this},i.prototype._finalizeQueryString=function(){var t=this._query.join("&");if(t&&(this.url+=(this.url.indexOf("?")>=0?"&":"?")+t),this._query.length=0,this._sort){var e=this.url.indexOf("?");if(e>=0){var n=this.url.substring(e+1).split("&");"function"===typeof this._sort?n.sort(this._sort):n.sort(),this.url=this.url.substring(0,e)+"?"+n.join("&")}}},i.prototype._appendQueryString=function(){console.trace("Unsupported")},i.prototype._timeoutError=function(t,e,n){if(!this._aborted){var r=new Error(t+e+"ms exceeded");r.timeout=e,r.code="ECONNABORTED",r.errno=n,this.timedout=!0,this.abort(),this.callback(r)}},i.prototype._setTimeouts=function(){var t=this;this._timeout&&!this._timer&&(this._timer=setTimeout(function(){t._timeoutError("Timeout of ",t._timeout,"ETIME")},this._timeout)),this._responseTimeout&&!this._responseTimeoutTimer&&(this._responseTimeoutTimer=setTimeout(function(){t._timeoutError("Response timeout of ",t._responseTimeout,"ETIMEDOUT")},this._responseTimeout))}},m0Pp:function(t,e,n){var r=n("2OiF");t.exports=function(t,e,n){if(r(t),void 0===e)return t;switch(n){case 1:return function(n){return t.call(e,n)};case 2:return function(n,r){return t.call(e,n,r)};case 3:return function(n,r,i){return t.call(e,n,r,i)}}return function(){return t.apply(e,arguments)}}},nGyu:function(t,e,n){var r=n("K0xU")("unscopables"),i=Array.prototype;void 0==i[r]&&n("Mukb")(i,r,{}),t.exports=function(t){i[r][t]=!0}},nICZ:function(t,e){t.exports=function(t){try{return{e:!1,v:t()}}catch(t){return{e:!0,v:t}}}},nZbv:function(t,e){function n(){this._defaults=[]}["use","on","once","set","query","type","accept","auth","withCredentials","sortQuery","retry","ok","redirects","timeout","buffer","serialize","parse","ca","key","pfx","cert"].forEach(function(t){n.prototype[t]=function(){return this._defaults.push({fn:t,arguments:arguments}),this}}),n.prototype._setDefaults=function(t){this._defaults.forEach(function(e){t[e.fn].apply(t,e.arguments)})},t.exports=n},ne8i:function(t,e,n){var r=n("RYi7"),i=Math.min;t.exports=function(t){return t>0?i(r(t),9007199254740991):0}},nh4g:function(t,e,n){t.exports=!n("eeVq")(function(){return 7!=Object.defineProperty({},"a",{get:function(){return 7}}).a})},oHnp:function(t,e,n){"use strict";e.type=function(t){return t.split(/ *; */).shift()},e.params=function(t){return t.split(/ *; */).reduce(function(t,e){var n=e.split(/ *= */),r=n.shift(),i=n.shift();return r&&i&&(t[r]=i),t},{})},e.parseLinks=function(t){return t.split(/ *, */).reduce(function(t,e){var n=e.split(/ *; */),r=n[0].slice(1,-1),i=n[1].split(/ *= */)[1].slice(1,-1);return t[i]=r,t},{})},e.cleanHeader=function(t,e){return delete t["content-type"],delete t["content-length"],delete t["transfer-encoding"],delete t["host"],e&&(delete t["authorization"],delete t["cookie"]),t}},ol8x:function(t,e,n){var r=n("dyZX"),i=r.navigator;t.exports=i&&i.userAgent||""},oyJW:function(t,e,n){"use strict";function r(t,e,n){return e in t?Object.defineProperty(t,e,{value:n,enumerable:!0,configurable:!0,writable:!0}):t[e]=n,t}n.d(e,"a",function(){return r})},pbhE:function(t,e,n){"use strict";var r=n("2OiF");function i(t){var e,n;this.promise=new t(function(t,r){if(void 0!==e||void 0!==n)throw TypeError("Bad Promise constructor");e=t,n=r}),this.resolve=r(e),this.reject=r(n)}t.exports.f=function(t){return new i(t)}},quPj:function(t,e,n){var r=n("0/R4"),i=n("LZWt"),o=n("K0xU")("match");t.exports=function(t){var e;return r(t)&&(void 0!==(e=t[o])?!!e:"RegExp"==i(t))}},rGqo:function(t,e,n){for(var r=n("yt8O"),i=n("DVgA"),o=n("KroJ"),a=n("dyZX"),u=n("Mukb"),s=n("hPIQ"),c=n("K0xU"),l=c("iterator"),f=c("toStringTag"),p=s.Array,h={CSSRuleList:!0,CSSStyleDeclaration:!1,CSSValueList:!1,ClientRectList:!1,DOMRectList:!1,DOMStringList:!1,DOMTokenList:!0,DataTransferItemList:!1,FileList:!1,HTMLAllCollection:!1,HTMLCollection:!1,HTMLFormElement:!1,HTMLSelectElement:!1,MediaList:!0,MimeTypeArray:!1,NamedNodeMap:!1,NodeList:!0,PaintRequestList:!1,Plugin:!1,PluginArray:!1,SVGLengthList:!1,SVGNumberList:!1,SVGPathSegList:!1,SVGPointList:!1,SVGStringList:!1,SVGTransformList:!1,SourceBufferList:!1,StyleSheetList:!0,TextTrackCueList:!1,TextTrackList:!1,TouchList:!1},d=i(h),v=0;vl)if(u=s[l++],u!=u)return!0}else for(;c>l;l++)if((t||l in s)&&s[l]===n)return t||l||0;return!t&&-1}}},xpql:function(t,e,n){t.exports=!n("nh4g")&&!n("eeVq")(function(){return 7!=Object.defineProperty(n("Iw71")("div"),"a",{get:function(){return 7}}).a})},y3w9:function(t,e,n){var r=n("0/R4");t.exports=function(t){if(!r(t))throw TypeError(t+" is not an object!");return t}},yLpj:function(t,e){var n;n=function(){return this}();try{n=n||Function("return this")()||(0,eval)("this")}catch(t){"object"===typeof window&&(n=window)}t.exports=n},yT7P:function(t,e,n){"use strict";n.d(e,"a",function(){return i});var r=n("oyJW");function i(t){for(var e=1;e=t.length?(this._t=void 0,i(1)):i(0,"keys"==e?n:"values"==e?t[n]:[n,t[n]])},"values"),o.Arguments=o.Array,r("keys"),r("values"),r("entries")},zRwo:function(t,e,n){var r=n("6FMO");t.exports=function(t,e){return new(r(t))(e)}},zhAb:function(t,e,n){var r=n("aagx"),i=n("aCFj"),o=n("w2a5")(!1),a=n("YTvA")("IE_PROTO");t.exports=function(t,e){var n,u=i(t),s=0,c=[];for(n in u)n!=a&&r(u,n)&&c.push(n);while(e.length>s)r(u,n=e[s++])&&(~o(c,n)||c.push(n));return c}}}]); +//# sourceMappingURL=chunk-vendors.b9a11975.js.map \ No newline at end of file diff --git a/web/vue/dist/chunk-vendors.b9a11975.js.map b/web/vue/dist/chunk-vendors.b9a11975.js.map new file mode 100644 index 0000000..167d6e6 --- /dev/null +++ b/web/vue/dist/chunk-vendors.b9a11975.js.map @@ -0,0 +1 @@ +{"version":3,"sources":["webpack:///./node_modules/core-js/modules/_html.js","webpack:///./node_modules/superagent/lib/response-base.js","webpack:///./node_modules/core-js/modules/_is-object.js","webpack:///./node_modules/core-js/modules/_string-context.js","webpack:///./node_modules/core-js/modules/_iter-step.js","webpack:///./node_modules/superagent/lib/client.js","webpack:///./node_modules/core-js/modules/_a-function.js","webpack:///./node_modules/core-js/modules/_redefine-all.js","webpack:///./node_modules/core-js/modules/_enum-bug-keys.js","webpack:///./node_modules/core-js/modules/_species-constructor.js","webpack:///./node_modules/core-js/modules/_array-species-constructor.js","webpack:///./node_modules/superagent/lib/is-object.js","webpack:///./node_modules/core-js/modules/es6.object.assign.js","webpack:///./node_modules/core-js/modules/_an-instance.js","webpack:///./node_modules/core-js/modules/_iter-define.js","webpack:///./node_modules/core-js/modules/_flags.js","webpack:///./node_modules/core-js/modules/_array-methods.js","webpack:///./node_modules/core-js/modules/_object-keys.js","webpack:///./node_modules/marked/lib/marked.js","webpack:///./node_modules/core-js/modules/_is-array.js","webpack:///./node_modules/core-js/modules/_object-dps.js","webpack:///./node_modules/core-js/modules/_task.js","webpack:///./node_modules/core-js/modules/_iter-call.js","webpack:///./node_modules/core-js/modules/_classof.js","webpack:///./node_modules/core-js/modules/es6.array.find-index.js","webpack:///./node_modules/core-js/modules/_dom-create.js","webpack:///./node_modules/core-js/modules/core.get-iterator-method.js","webpack:///./node_modules/core-js/modules/_object-gops.js","webpack:///./node_modules/core-js/modules/_wks.js","webpack:///./node_modules/vue-loader/lib/runtime/componentNormalizer.js","webpack:///./node_modules/core-js/modules/_redefine.js","webpack:///./node_modules/core-js/modules/_object-create.js","webpack:///./node_modules/vue/dist/vue.runtime.esm.js","webpack:///./node_modules/vuex/dist/vuex.esm.js","webpack:///./node_modules/core-js/modules/es6.string.includes.js","webpack:///./node_modules/core-js/modules/_library.js","webpack:///./node_modules/core-js/modules/_cof.js","webpack:///./node_modules/lodash/lodash.js","webpack:///./node_modules/core-js/modules/_strict-method.js","webpack:///./node_modules/core-js/modules/_is-array-iter.js","webpack:///./node_modules/core-js/modules/_invoke.js","webpack:///./node_modules/core-js/modules/_hide.js","webpack:///./node_modules/core-js/modules/es6.regexp.flags.js","webpack:///./node_modules/core-js/modules/_object-gpo.js","webpack:///./node_modules/core-js/modules/_iter-create.js","webpack:///./node_modules/core-js/modules/_to-integer.js","webpack:///./node_modules/core-js/modules/_property-desc.js","webpack:///./node_modules/core-js/modules/_to-object.js","webpack:///./node_modules/core-js/modules/_for-of.js","webpack:///./node_modules/superagent-no-cache/index.js","webpack:///./node_modules/core-js/modules/_fails-is-regexp.js","webpack:///./node_modules/core-js/modules/_object-pie.js","webpack:///./node_modules/core-js/modules/es6.promise.js","webpack:///./node_modules/core-js/modules/_shared.js","webpack:///./node_modules/core-js/modules/es6.array.sort.js","webpack:///./node_modules/core-js/modules/_export.js","webpack:///./node_modules/core-js/modules/_iter-detect.js","webpack:///./node_modules/core-js/modules/_shared-key.js","webpack:///./node_modules/core-js/modules/_iobject.js","webpack:///(webpack)/buildin/module.js","webpack:///./node_modules/core-js/modules/es7.array.includes.js","webpack:///./node_modules/core-js/modules/es6.regexp.to-string.js","webpack:///./node_modules/core-js/modules/_to-iobject.js","webpack:///./node_modules/core-js/modules/_has.js","webpack:///./node_modules/core-js/modules/_to-primitive.js","webpack:///./node_modules/component-emitter/index.js","webpack:///./node_modules/core-js/modules/_object-assign.js","webpack:///./node_modules/core-js/modules/_to-absolute-index.js","webpack:///./node_modules/core-js/modules/es6.array.find.js","webpack:///./node_modules/core-js/modules/_global.js","webpack:///./node_modules/core-js/modules/_fails.js","webpack:///./node_modules/core-js/modules/_set-species.js","webpack:///./node_modules/core-js/modules/es6.function.name.js","webpack:///./node_modules/core-js/modules/_set-to-string-tag.js","webpack:///./node_modules/core-js/modules/_core.js","webpack:///./node_modules/core-js/modules/_microtask.js","webpack:///./node_modules/core-js/modules/_iterators.js","webpack:///./node_modules/component-ie/index.js","webpack:///./node_modules/core-js/modules/_object-dp.js","webpack:///./node_modules/@babel/runtime/helpers/builtin/es6/arrayWithoutHoles.js","webpack:///./node_modules/@babel/runtime/helpers/builtin/es6/iterableToArray.js","webpack:///./node_modules/@babel/runtime/helpers/builtin/es6/nonIterableSpread.js","webpack:///./node_modules/@babel/runtime/helpers/builtin/es6/toConsumableArray.js","webpack:///./node_modules/vue-router/dist/vue-router.esm.js","webpack:///./node_modules/@babel/runtime/helpers/builtin/es6/arrayWithHoles.js","webpack:///./node_modules/@babel/runtime/helpers/builtin/es6/iterableToArrayLimit.js","webpack:///./node_modules/@babel/runtime/helpers/builtin/es6/nonIterableRest.js","webpack:///./node_modules/@babel/runtime/helpers/builtin/es6/slicedToArray.js","webpack:///./node_modules/superagent/lib/request-base.js","webpack:///./node_modules/core-js/modules/_ctx.js","webpack:///./node_modules/core-js/modules/_add-to-unscopables.js","webpack:///./node_modules/core-js/modules/_perform.js","webpack:///./node_modules/superagent/lib/agent-base.js","webpack:///./node_modules/core-js/modules/_to-length.js","webpack:///./node_modules/core-js/modules/_descriptors.js","webpack:///./node_modules/superagent/lib/utils.js","webpack:///./node_modules/core-js/modules/_user-agent.js","webpack:///./node_modules/@babel/runtime/helpers/builtin/es6/defineProperty.js","webpack:///./node_modules/core-js/modules/_new-promise-capability.js","webpack:///./node_modules/core-js/modules/_is-regexp.js","webpack:///./node_modules/core-js/modules/web.dom.iterable.js","webpack:///./node_modules/core-js/modules/_promise-resolve.js","webpack:///./node_modules/core-js/modules/_defined.js","webpack:///./node_modules/core-js/modules/_array-includes.js","webpack:///./node_modules/core-js/modules/_ie8-dom-define.js","webpack:///./node_modules/core-js/modules/_an-object.js","webpack:///(webpack)/buildin/global.js","webpack:///./node_modules/@babel/runtime/helpers/builtin/es6/objectSpread.js","webpack:///./node_modules/core-js/modules/_uid.js","webpack:///./node_modules/core-js/modules/es6.array.iterator.js","webpack:///./node_modules/core-js/modules/_array-species-create.js","webpack:///./node_modules/core-js/modules/_object-keys-internal.js"],"names":["document","__webpack_require__","module","exports","documentElement","utils","ResponseBase","obj","mixin","key","prototype","get","field","this","header","toLowerCase","_setHeaderProperties","ct","type","params","links","link","parseLinks","err","_setStatusProperties","status","statusCode","statusType","info","ok","redirect","clientError","serverError","error","toError","created","accepted","noContent","badRequest","unauthorized","notAcceptable","forbidden","notFound","unprocessableEntity","it","isRegExp","defined","that","searchString","NAME","TypeError","String","done","value","root","window","self","console","warn","Emitter","RequestBase","isObject","Agent","noop","request","method","url","Request","end","arguments","length","getXHR","XMLHttpRequest","location","protocol","ActiveXObject","e","Error","trim","s","replace","serialize","pairs","pushEncodedKeyValuePair","join","val","Array","isArray","forEach","v","subkey","push","encodeURIComponent","parseString","str","pair","pos","split","i","len","indexOf","decodeURIComponent","slice","parseHeader","index","line","lines","fields","isJSON","mime","test","Response","req","xhr","text","responseType","responseText","statusText","headers","getAllResponseHeaders","getResponseHeader","_responseType","body","response","_parseBody","_query","_header","on","new_err","res","parse","original","rawResponse","callback","emit","_isResponseOK","custom_err","del","data","fn","send","serializeObject","types","html","json","xml","urlencoded","form","form-data","application/x-www-form-urlencoded","application/json","JSON","stringify","_parser","Object","msg","set","accept","auth","user","pass","options","btoa","encoder","string","_auth","query","attach","file","_data","_getFormData","append","name","_formData","FormData","_shouldRetry","_retry","_callback","clearTimeout","_maxRetries","retries","_retries","crossDomainError","crossDomain","buffer","ca","agent","pipe","write","_isHost","toString","call","_endCalled","_finalizeQueryString","_end","_setTimeouts","onreadystatechange","readyState","_responseTimeoutTimer","timedout","_aborted","handleProgress","direction","total","percent","loaded","hasListeners","onprogress","bind","upload","username","password","open","_withCredentials","withCredentials","contentType","_serializer","hasOwnProperty","setRequestHeader","_setDefaults","head","patch","post","put","redefine","target","src","safe","anObject","aFunction","SPECIES","O","D","S","C","constructor","undefined","$export","F","assign","Constructor","forbiddenField","LIBRARY","hide","Iterators","$iterCreate","setToStringTag","getPrototypeOf","ITERATOR","BUGGY","keys","FF_ITERATOR","KEYS","VALUES","returnThis","Base","next","DEFAULT","IS_SET","FORCED","methods","IteratorPrototype","getMethod","kind","proto","TAG","DEF_VALUES","VALUES_BUG","$native","$default","$entries","$anyNative","entries","values","P","result","global","ignoreCase","multiline","unicode","sticky","ctx","IObject","toObject","toLength","asc","TYPE","$create","IS_MAP","IS_FILTER","IS_SOME","IS_EVERY","IS_FIND_INDEX","NO_HOLES","create","$this","callbackfn","f","$keys","enumBugKeys","block","newline","code","fences","hr","heading","nptable","blockquote","list","def","table","lheading","paragraph","Lexer","tokens","marked","defaults","rules","normal","pedantic","gfm","tables","_label","_title","edit","getRegex","bullet","item","source","_tag","_comment","merge","lex","lexer","token","top","loose","cap","bull","b","space","tag","l","isordered","istask","ischecked","exec","substring","lang","depth","splitCells","align","cells","ordered","start","match","RegExp","smartLists","charAt","task","checked","sanitize","pre","sanitizer","href","title","charCodeAt","inline","escape","autolink","reflink","nolink","strong","em","br","InlineLexer","renderer","Renderer","breaks","TextRenderer","Parser","encode","unescape","_","n","fromCharCode","parseInt","regex","opt","resolveUrl","base","baseUrls","_escapes","_scheme","_email","_attribute","_href","_backpedal","output","out","mangle","inLink","outputLink","escapes","codespan","smartypants","image","ch","Math","random","escaped","highlight","langPrefix","quote","level","raw","headerIds","headerPrefix","xhtml","startatt","listitem","checkbox","tablerow","content","tablecell","flags","prot","baseUrl","originIndependentUrl","encodeURI","parser","inlineText","reverse","tok","pop","peek","parseText","row","cell","j","tableRow","count","splice","pending","message","silent","setOptions","getDefaults","inlineLexer","cof","arg","dP","getKeys","defineProperties","Properties","defer","channel","port","invoke","cel","process","setTask","setImmediate","clearTask","clearImmediate","MessageChannel","Dispatch","counter","queue","ONREADYSTATECHANGE","run","id","listener","event","args","Function","nextTick","now","port2","port1","onmessage","postMessage","addEventListener","importScripts","appendChild","removeChild","setTimeout","clear","iterator","ret","ARG","tryGet","T","B","callee","$find","KEY","forced","findIndex","is","createElement","classof","getIteratorMethod","getOwnPropertySymbols","store","uid","Symbol","USE_SYMBOL","$exports","normalizeComponent","scriptExports","render","staticRenderFns","functionalTemplate","injectStyles","scopeId","moduleIdentifier","shadowMode","hook","_compiled","functional","_scopeId","context","$vnode","ssrContext","parent","__VUE_SSR_CONTEXT__","_registeredComponents","add","_ssrRegister","$root","$options","shadowRoot","_injectStyles","originalRender","h","existing","beforeCreate","concat","d","__webpack_exports__","has","SRC","TO_STRING","$toString","TPL","inspectSource","isFunction","dPs","IE_PROTO","Empty","PROTOTYPE","createDict","iframeDocument","iframe","lt","gt","style","display","contentWindow","close","emptyObject","freeze","isUndef","isDef","isTrue","isFalse","isPrimitive","_toString","isPlainObject","isValidArrayIndex","parseFloat","floor","isFinite","toNumber","isNaN","makeMap","expectsLowerCase","map","isReservedAttribute","remove","arr","hasOwn","cached","cache","hit","camelizeRE","camelize","c","toUpperCase","capitalize","hyphenateRE","hyphenate","polyfillBind","boundFn","a","apply","_length","nativeBind","toArray","extend","to","_from","no","identity","looseEqual","isObjectA","isObjectB","isArrayA","isArrayB","every","keysA","keysB","looseIndexOf","once","called","SSR_ATTR","ASSET_TYPES","LIFECYCLE_HOOKS","config","optionMergeStrategies","productionTip","devtools","performance","errorHandler","warnHandler","ignoredElements","keyCodes","isReservedTag","isReservedAttr","isUnknownElement","getTagNamespace","parsePlatformTagName","mustUseProp","_lifecycleHooks","isReserved","enumerable","defineProperty","writable","configurable","bailRE","parsePath","path","segments","_isServer","hasProto","inBrowser","inWeex","WXEnvironment","platform","weexPlatform","UA","navigator","userAgent","isIE","isIE9","isEdge","isIOS","nativeWatch","watch","supportsPassive","opts","isServerRendering","env","VUE_ENV","__VUE_DEVTOOLS_GLOBAL_HOOK__","isNative","Ctor","_Set","hasSymbol","Reflect","ownKeys","Set","Dep","subs","addSub","sub","removeSub","depend","addDep","notify","update","targetStack","pushTarget","_target","popTarget","VNode","children","elm","componentOptions","asyncFactory","ns","fnContext","fnOptions","fnScopeId","componentInstance","isStatic","isRootInsert","isComment","isCloned","isOnce","asyncMeta","isAsyncPlaceholder","prototypeAccessors","child","createEmptyVNode","node","createTextVNode","cloneVNode","vnode","cloned","arrayProto","arrayMethods","methodsToPatch","inserted","ob","__ob__","observeArray","dep","arrayKeys","getOwnPropertyNames","shouldObserve","toggleObserving","Observer","vmCount","augment","protoAugment","copyAugment","walk","__proto__","observe","asRootData","isExtensible","_isVue","defineReactive","customSetter","shallow","property","getOwnPropertyDescriptor","getter","setter","childOb","dependArray","newVal","max","items","strats","mergeData","from","toVal","fromVal","mergeDataOrFn","parentVal","childVal","vm","instanceData","defaultData","mergeHook","mergeAssets","key$1","props","inject","computed","provide","defaultStrat","normalizeProps","normalizeInject","normalized","normalizeDirectives","dirs","directives","mergeOptions","extendsFrom","extends","mixins","mergeField","strat","resolveAsset","warnMissing","assets","camelizedId","PascalCaseId","validateProp","propOptions","propsData","prop","absent","booleanIndex","getTypeIndex","Boolean","stringIndex","getPropDefaultValue","prevShouldObserve","default","_props","getType","isSameType","expectedTypes","handleError","cur","$parent","hooks","errorCaptured","capture","globalHandleError","logError","microTimerFunc","macroTimerFunc","callbacks","flushCallbacks","copies","useMacroTask","Promise","p","resolve","then","withMacroTask","_withTask","cb","_resolve","seenObjects","traverse","_traverse","seen","isA","isFrozen","depId","normalizeEvent","passive","once$$1","createFnInvoker","fns","invoker","arguments$1","updateListeners","oldOn","remove$$1","old","mergeVNodeHook","hookKey","oldHook","wrappedHook","merged","extractPropsFromVNodeData","attrs","altKey","checkProp","hash","preserve","simpleNormalizeChildren","normalizeChildren","normalizeArrayChildren","isTextNode","nestedIndex","lastIndex","last","shift","_isVList","ensureCtor","comp","__esModule","toStringTag","createAsyncPlaceholder","factory","resolveAsyncComponent","baseCtor","errorComp","resolved","loading","loadingComp","contexts","sync","forceRender","$forceUpdate","reject","reason","component","delay","timeout","getFirstComponentChild","initEvents","_events","_hasHookEvent","listeners","_parentListeners","updateComponentListeners","$once","$on","remove$1","$off","oldListeners","eventsMixin","Vue","hookRE","this$1","cbs","i$1","$emit","resolveSlots","slots","slot","name$1","isWhitespace","resolveScopedSlots","activeInstance","initLifecycle","abstract","$children","$refs","_watcher","_inactive","_directInactive","_isMounted","_isDestroyed","_isBeingDestroyed","lifecycleMixin","_update","hydrating","callHook","prevEl","$el","prevVnode","_vnode","prevActiveInstance","__patch__","_parentElm","_refElm","__vue__","$destroy","teardown","_watchers","mountComponent","el","updateComponent","_render","Watcher","updateChildComponent","parentVnode","renderChildren","hasChildren","_renderChildren","scopedSlots","$scopedSlots","_parentVnode","$attrs","$listeners","propKeys","_propKeys","$slots","isInInactiveTree","activateChildComponent","direct","deactivateChildComponent","handlers","activatedChildren","waiting","flushing","resetSchedulerState","flushSchedulerQueue","watcher","sort","activatedQueue","updatedQueue","callActivatedHooks","callUpdatedHooks","queueActivatedComponent","queueWatcher","uid$1","expOrFn","isRenderWatcher","deep","lazy","active","dirty","deps","newDeps","depIds","newDepIds","expression","cleanupDeps","tmp","oldValue","evaluate","sharedPropertyDefinition","proxy","sourceKey","initState","initProps","initMethods","initData","initComputed","initWatch","propsOptions","isRoot","loop","getData","computedWatcherOptions","watchers","_computedWatchers","isSSR","userDef","defineComputed","shouldCache","createComputedGetter","handler","createWatcher","$watch","stateMixin","dataDef","propsDef","$set","$delete","immediate","initProvide","_provided","initInjections","resolveInject","filter","provideKey","provideDefault","renderList","renderSlot","fallback","bindObject","nodes","scopedSlotFn","slotNodes","_rendered","$createElement","resolveFilter","isKeyNotMatch","expect","actual","checkKeyCodes","eventKeyCode","builtInKeyCode","eventKeyName","builtInKeyName","mappedKeyCode","bindObjectProps","asProp","isSync","domProps","$event","renderStatic","isInFor","_staticTrees","tree","_renderProxy","markStatic","markOnce","markStaticNode","bindObjectListeners","ours","installRenderHelpers","_o","_n","_s","_l","_t","_q","_i","_m","_f","_k","_b","_v","_e","_u","_g","FunctionalRenderContext","contextVm","_original","isCompiled","needNormalization","injections","_c","createFunctionalComponent","mergeProps","renderContext","cloneAndMarkFunctionalResult","vnodes","clone","componentVNodeHooks","init","parentElm","refElm","keepAlive","mountedNode","prepatch","createComponentInstanceForVnode","$mount","oldVnode","insert","destroy","hooksToMerge","createComponent","_base","cid","resolveConstructorOptions","model","transformModel","nativeOn","installComponentHooks","_isComponent","inlineTemplate","SIMPLE_NORMALIZE","ALWAYS_NORMALIZE","normalizationType","alwaysNormalize","_createElement","applyNS","registerDeepBindings","force","class","initRender","parentData","renderMixin","$nextTick","ref","uid$3","initMixin","_init","_uid","initInternalComponent","_self","vnodeComponentOptions","_componentTag","super","superOptions","cachedSuperOptions","modifiedOptions","resolveModifiedOptions","extendOptions","components","modified","latest","extended","sealed","sealedOptions","dedupe","initUse","use","plugin","installedPlugins","_installedPlugins","unshift","install","initMixin$1","initExtend","Super","SuperId","cachedCtors","_Ctor","Sub","initProps$1","initComputed$1","Comp","initAssetRegisters","definition","getComponentName","matches","pattern","pruneCache","keepAliveInstance","cachedNode","pruneCacheEntry","current","cached$$1","patternTypes","KeepAlive","include","exclude","Number","destroyed","mounted","ref$1","builtInComponents","initGlobalAPI","configDef","util","delete","version","acceptValue","attr","isEnumeratedAttr","isBooleanAttr","xlinkNS","isXlink","getXlinkProp","isFalsyAttrValue","genClassForVnode","parentNode","childNode","mergeClassData","renderClass","staticClass","dynamicClass","stringifyClass","stringifyArray","stringifyObject","stringified","namespaceMap","svg","math","isHTMLTag","isSVG","unknownElementCache","HTMLUnknownElement","HTMLElement","isTextInputType","selected","querySelector","createElement$1","tagName","multiple","setAttribute","createElementNS","namespace","createTextNode","createComment","insertBefore","newNode","referenceNode","nextSibling","setTextContent","textContent","setStyleScope","nodeOps","registerRef","isRemoval","refs","refInFor","emptyNode","sameVnode","sameInputType","typeA","typeB","createKeyToOldIdx","beginIdx","endIdx","createPatchFunction","backend","modules","emptyNodeAt","createRmCb","childElm","removeNode","createElm","insertedVnodeQueue","nested","ownerArray","setScope","createChildren","invokeCreateHooks","isReactivated","initComponent","reactivateComponent","pendingInsert","isPatchable","innerNode","transition","activate","ref$$1","ancestor","addVnodes","startIdx","invokeDestroyHook","removeVnodes","removeAndInvokeRemoveHook","rm","updateChildren","oldCh","newCh","removeOnly","oldKeyToIdx","idxInOld","vnodeToMove","oldStartIdx","newStartIdx","oldEndIdx","oldStartVnode","oldEndVnode","newEndIdx","newStartVnode","newEndVnode","canMove","patchVnode","findIdxInOld","hydrate","postpatch","invokeInsertHook","initial","isRenderedModule","inVPre","hasChildNodes","innerHTML","childrenMatch","firstChild","fullInvoke","isInitialPatch","isRealElement","nodeType","hasAttribute","removeAttribute","oldElm","parentElm$1","_leaveCb","patchable","i$2","updateDirectives","oldDir","dir","isCreate","isDestroy","oldDirs","normalizeDirectives$1","newDirs","dirsWithInsert","dirsWithPostpatch","callHook$1","componentUpdated","callInsert","emptyModifiers","modifiers","getRawDirName","rawName","baseModules","updateAttrs","inheritAttrs","oldAttrs","setAttr","removeAttributeNS","baseSetAttr","setAttributeNS","__ieph","blocker","stopImmediatePropagation","removeEventListener","updateClass","oldData","cls","transitionClass","_transitionClasses","_prevClass","target$1","klass","RANGE_TOKEN","CHECKBOX_RADIO_TOKEN","normalizeEvents","change","createOnceHandler","onceHandler","remove$2","add$1","updateDOMListeners","events","updateDOMProps","oldProps","childNodes","_value","strCur","shouldUpdateValue","checkVal","composing","isNotInFocusAndDirty","isDirtyWithModifiers","notInFocus","activeElement","_vModifiers","number","parseStyleText","cssText","listDelimiter","propertyDelimiter","normalizeStyleData","normalizeStyleBinding","staticStyle","bindingStyle","getStyle","checkChild","styleData","emptyStyle","cssVarRE","importantRE","setProp","setProperty","normalizedName","normalize","vendorNames","capName","updateStyle","oldStaticStyle","oldStyleBinding","normalizedStyle","oldStyle","newStyle","addClass","classList","getAttribute","removeClass","tar","resolveTransition","css","autoCssTransition","enterClass","enterToClass","enterActiveClass","leaveClass","leaveToClass","leaveActiveClass","hasTransition","TRANSITION","ANIMATION","transitionProp","transitionEndEvent","animationProp","animationEndEvent","ontransitionend","onwebkittransitionend","onanimationend","onwebkitanimationend","raf","requestAnimationFrame","nextFrame","addTransitionClass","transitionClasses","removeTransitionClass","whenTransitionEnds","expectedType","getTransitionInfo","propCount","ended","onEnd","transformRE","styles","getComputedStyle","transitionDelays","transitionDurations","transitionTimeout","getTimeout","animationDelays","animationDurations","animationTimeout","hasTransform","delays","durations","toMs","enter","toggleDisplay","cancelled","_enterCb","appearClass","appearToClass","appearActiveClass","beforeEnter","afterEnter","enterCancelled","beforeAppear","appear","afterAppear","appearCancelled","duration","transitionNode","isAppear","startClass","activeClass","toClass","beforeEnterHook","enterHook","afterEnterHook","enterCancelledHook","explicitEnterDuration","expectsCSS","userWantsControl","getHookArgumentsLength","show","pendingNode","_pending","isValidDuration","leave","beforeLeave","afterLeave","leaveCancelled","delayLeave","explicitLeaveDuration","performLeave","invokerFns","_enter","platformModules","vmodel","trigger","directive","binding","_vOptions","setSelected","getValue","onCompositionStart","onCompositionEnd","prevOptions","curOptions","some","o","needReset","hasNoMatchingOption","actuallySetSelected","isMultiple","option","selectedIndex","createEvent","initEvent","dispatchEvent","locateNode","transition$$1","originalDisplay","__vOriginalDisplay","unbind","platformDirectives","transitionProps","mode","getRealChild","compOptions","extractTransitionData","placeholder","rawChild","hasParentTransition","isSameChild","oldChild","Transition","_leaving","oldRawChild","delayedLeave","moveClass","TransitionGroup","prevChildren","rawChildren","transitionData","kept","removed","c$1","getBoundingClientRect","beforeUpdate","updated","hasMove","callPendingCbs","recordPosition","applyTranslation","_reflow","offsetHeight","moved","transform","WebkitTransform","transitionDuration","_moveCb","propertyName","_hasMove","cloneNode","newPos","oldPos","dx","left","dy","platformComponents","applyMixin","vuexInit","$store","devtoolHook","devtoolPlugin","_devtoolHook","targetState","replaceState","subscribe","mutation","state","forEachValue","isPromise","Module","rawModule","runtime","_children","_rawModule","rawState","prototypeAccessors$1","namespaced","addChild","getChild","actions","mutations","getters","forEachChild","forEachGetter","forEachAction","forEachMutation","ModuleCollection","rawRootModule","register","targetModule","newModule","reduce","getNamespace","rawChildModule","unregister","Store","plugins","strict","_committing","_actions","_actionSubscribers","_mutations","_wrappedGetters","_modules","_modulesNamespaceMap","_subscribers","_watcherVM","dispatch","commit","payload","installModule","resetStoreVM","genericSubscribe","resetStore","hot","oldVm","_vm","wrappedGetters","$$state","enableStrictMode","_withCommit","rootState","parentState","getNestedState","moduleName","local","makeLocalContext","namespacedType","registerMutation","action","registerAction","registerGetter","noNamespace","_type","_payload","_options","unifyObjectStyle","makeLocalGetters","gettersProxy","splitPos","localType","entry","rootGetters","catch","rawGetter","_Vue","all","subscribeAction","registerModule","preserveState","unregisterModule","hotUpdate","newOptions","committing","mapState","normalizeNamespace","states","normalizeMap","getModuleByNamespace","vuex","mapMutations","mapGetters","mapActions","createNamespacedHelpers","helper","index_esm","INCLUDES","includes","__WEBPACK_AMD_DEFINE_RESULT__","VERSION","LARGE_ARRAY_SIZE","CORE_ERROR_TEXT","FUNC_ERROR_TEXT","HASH_UNDEFINED","MAX_MEMOIZE_SIZE","PLACEHOLDER","CLONE_DEEP_FLAG","CLONE_FLAT_FLAG","CLONE_SYMBOLS_FLAG","COMPARE_PARTIAL_FLAG","COMPARE_UNORDERED_FLAG","WRAP_BIND_FLAG","WRAP_BIND_KEY_FLAG","WRAP_CURRY_BOUND_FLAG","WRAP_CURRY_FLAG","WRAP_CURRY_RIGHT_FLAG","WRAP_PARTIAL_FLAG","WRAP_PARTIAL_RIGHT_FLAG","WRAP_ARY_FLAG","WRAP_REARG_FLAG","WRAP_FLIP_FLAG","DEFAULT_TRUNC_LENGTH","DEFAULT_TRUNC_OMISSION","HOT_COUNT","HOT_SPAN","LAZY_FILTER_FLAG","LAZY_MAP_FLAG","LAZY_WHILE_FLAG","INFINITY","MAX_SAFE_INTEGER","MAX_INTEGER","NAN","MAX_ARRAY_LENGTH","MAX_ARRAY_INDEX","HALF_MAX_ARRAY_LENGTH","wrapFlags","argsTag","arrayTag","asyncTag","boolTag","dateTag","domExcTag","errorTag","funcTag","genTag","mapTag","numberTag","nullTag","objectTag","promiseTag","proxyTag","regexpTag","setTag","stringTag","symbolTag","undefinedTag","weakMapTag","weakSetTag","arrayBufferTag","dataViewTag","float32Tag","float64Tag","int8Tag","int16Tag","int32Tag","uint8Tag","uint8ClampedTag","uint16Tag","uint32Tag","reEmptyStringLeading","reEmptyStringMiddle","reEmptyStringTrailing","reEscapedHtml","reUnescapedHtml","reHasEscapedHtml","reHasUnescapedHtml","reEscape","reEvaluate","reInterpolate","reIsDeepProp","reIsPlainProp","rePropName","reRegExpChar","reHasRegExpChar","reTrim","reTrimStart","reTrimEnd","reWrapComment","reWrapDetails","reSplitDetails","reAsciiWord","reEscapeChar","reEsTemplate","reFlags","reIsBadHex","reIsBinary","reIsHostCtor","reIsOctal","reIsUint","reLatin","reNoMatch","reUnescapedString","rsAstralRange","rsComboMarksRange","reComboHalfMarksRange","rsComboSymbolsRange","rsComboRange","rsDingbatRange","rsLowerRange","rsMathOpRange","rsNonCharRange","rsPunctuationRange","rsSpaceRange","rsUpperRange","rsVarRange","rsBreakRange","rsApos","rsAstral","rsBreak","rsCombo","rsDigits","rsDingbat","rsLower","rsMisc","rsFitz","rsModifier","rsNonAstral","rsRegional","rsSurrPair","rsUpper","rsZWJ","rsMiscLower","rsMiscUpper","rsOptContrLower","rsOptContrUpper","reOptMod","rsOptVar","rsOptJoin","rsOrdLower","rsOrdUpper","rsSeq","rsEmoji","rsSymbol","reApos","reComboMark","reUnicode","reUnicodeWord","reHasUnicode","reHasUnicodeWord","contextProps","templateCounter","typedArrayTags","cloneableTags","deburredLetters","À","Á","Â","Ã","Ä","Å","à","á","â","ã","ä","å","Ç","ç","Ð","ð","È","É","Ê","Ë","è","é","ê","ë","Ì","Í","Î","Ï","ì","í","î","ï","Ñ","ñ","Ò","Ó","Ô","Õ","Ö","Ø","ò","ó","ô","õ","ö","ø","Ù","Ú","Û","Ü","ù","ú","û","ü","Ý","ý","ÿ","Æ","æ","Þ","þ","ß","Ā","Ă","Ą","ā","ă","ą","Ć","Ĉ","Ċ","Č","ć","ĉ","ċ","č","Ď","Đ","ď","đ","Ē","Ĕ","Ė","Ę","Ě","ē","ĕ","ė","ę","ě","Ĝ","Ğ","Ġ","Ģ","ĝ","ğ","ġ","ģ","Ĥ","Ħ","ĥ","ħ","Ĩ","Ī","Ĭ","Į","İ","ĩ","ī","ĭ","į","ı","Ĵ","ĵ","Ķ","ķ","ĸ","Ĺ","Ļ","Ľ","Ŀ","Ł","ĺ","ļ","ľ","ŀ","ł","Ń","Ņ","Ň","Ŋ","ń","ņ","ň","ŋ","Ō","Ŏ","Ő","ō","ŏ","ő","Ŕ","Ŗ","Ř","ŕ","ŗ","ř","Ś","Ŝ","Ş","Š","ś","ŝ","ş","š","Ţ","Ť","Ŧ","ţ","ť","ŧ","Ũ","Ū","Ŭ","Ů","Ű","Ų","ũ","ū","ŭ","ů","ű","ų","Ŵ","ŵ","Ŷ","ŷ","Ÿ","Ź","Ż","Ž","ź","ż","ž","IJ","ij","Œ","œ","ʼn","ſ","htmlEscapes","&","<",">","\"","'","htmlUnescapes","&","<",">",""","'","stringEscapes","\\","\n","\r","
","
","freeParseFloat","freeParseInt","freeGlobal","freeSelf","freeExports","freeModule","moduleExports","freeProcess","nodeUtil","require","nodeIsArrayBuffer","isArrayBuffer","nodeIsDate","isDate","nodeIsMap","isMap","nodeIsRegExp","nodeIsSet","isSet","nodeIsTypedArray","isTypedArray","func","thisArg","arrayAggregator","array","iteratee","accumulator","arrayEach","arrayEachRight","arrayEvery","predicate","arrayFilter","resIndex","arrayIncludes","baseIndexOf","arrayIncludesWith","comparator","arrayMap","arrayPush","offset","arrayReduce","initAccum","arrayReduceRight","arraySome","asciiSize","baseProperty","asciiToArray","asciiWords","baseFindKey","collection","eachFunc","baseFindIndex","fromIndex","fromRight","strictIndexOf","baseIsNaN","baseIndexOfWith","baseMean","baseSum","object","basePropertyOf","baseReduce","baseSortBy","comparer","baseTimes","baseToPairs","baseUnary","baseValues","cacheHas","charsStartIndex","strSymbols","chrSymbols","charsEndIndex","countHolders","deburrLetter","escapeHtmlChar","escapeStringChar","chr","hasUnicode","hasUnicodeWord","iteratorToArray","mapToArray","size","overArg","replaceHolders","safeGet","setToArray","setToPairs","strictLastIndexOf","stringSize","unicodeSize","stringToArray","unicodeToArray","unescapeHtmlChar","unicodeWords","runInContext","pick","Date","funcProto","objectProto","coreJsData","funcToString","idCounter","maskSrcKey","nativeObjectToString","objectCtorString","oldDash","reIsNative","Buffer","Uint8Array","allocUnsafe","getPrototype","objectCreate","propertyIsEnumerable","spreadableSymbol","isConcatSpreadable","symIterator","symToStringTag","getNative","ctxClearTimeout","ctxNow","ctxSetTimeout","nativeCeil","ceil","nativeFloor","nativeGetSymbols","nativeIsBuffer","isBuffer","nativeIsFinite","nativeJoin","nativeKeys","nativeMax","nativeMin","min","nativeNow","nativeParseInt","nativeRandom","nativeReverse","DataView","Map","WeakMap","nativeCreate","metaMap","realNames","dataViewCtorString","toSource","mapCtorString","promiseCtorString","setCtorString","weakMapCtorString","symbolProto","symbolValueOf","valueOf","symbolToString","lodash","isObjectLike","LazyWrapper","LodashWrapper","wrapperClone","baseCreate","baseLodash","chainAll","__wrapped__","__actions__","__chain__","__index__","__values__","__dir__","__filtered__","__iteratees__","__takeCount__","__views__","lazyClone","copyArray","lazyReverse","lazyValue","isArr","isRight","arrLength","view","getView","iteratees","iterLength","takeCount","baseWrapperValue","outer","iterIndex","Hash","hashClear","__data__","hashDelete","hashGet","hashHas","hashSet","ListCache","listCacheClear","listCacheDelete","assocIndexOf","listCacheGet","listCacheHas","listCacheSet","MapCache","mapCacheClear","mapCacheDelete","getMapData","mapCacheGet","mapCacheHas","mapCacheSet","SetCache","setCacheAdd","setCacheHas","Stack","stackClear","stackDelete","stackGet","stackHas","stackSet","arrayLikeKeys","inherited","isArg","isArguments","isBuff","isType","skipIndexes","isIndex","arraySample","baseRandom","arraySampleSize","shuffleSelf","baseClamp","arrayShuffle","assignMergeValue","eq","baseAssignValue","assignValue","objValue","baseAggregator","baseEach","baseAssign","copyObject","baseAssignIn","keysIn","baseAt","paths","skip","lower","upper","baseClone","bitmask","customizer","stack","isDeep","isFlat","isFull","initCloneArray","getTag","isFunc","cloneBuffer","initCloneObject","copySymbolsIn","copySymbols","initCloneByTag","stacked","subValue","keysFunc","getAllKeysIn","getAllKeys","baseConforms","baseConformsTo","baseDelay","wait","baseDifference","isCommon","valuesLength","valuesIndex","templateSettings","interpolate","variable","imports","createBaseEach","baseForOwn","baseEachRight","baseForOwnRight","baseEvery","baseExtremum","isSymbol","baseFill","toInteger","baseFilter","baseFlatten","isStrict","isFlattenable","baseFor","createBaseFor","baseForRight","baseFunctions","baseGet","castPath","toKey","baseGetAllKeys","symbolsFunc","baseGetTag","getRawTag","objectToString","baseGt","other","baseHas","baseHasIn","baseInRange","baseIntersection","arrays","othLength","othIndex","caches","maxLength","Infinity","baseInverter","baseInvoke","baseIsArguments","baseIsArrayBuffer","baseIsDate","baseIsEqual","baseIsEqualDeep","equalFunc","objIsArr","othIsArr","objTag","othTag","objIsObj","othIsObj","isSameTag","equalArrays","equalByTag","objIsWrapped","othIsWrapped","objUnwrapped","othUnwrapped","equalObjects","baseIsMap","baseIsMatch","matchData","noCustomizer","srcValue","baseIsNative","isMasked","baseIsRegExp","baseIsSet","baseIsTypedArray","isLength","baseIteratee","baseMatchesProperty","baseMatches","baseKeys","isPrototype","baseKeysIn","nativeKeysIn","isProto","baseLt","baseMap","isArrayLike","getMatchData","matchesStrictComparable","isKey","isStrictComparable","hasIn","baseMerge","srcIndex","baseMergeDeep","newValue","mergeFunc","isTyped","isArrayLikeObject","cloneTypedArray","toPlainObject","baseNth","baseOrderBy","orders","getIteratee","criteria","compareMultiple","basePick","basePickBy","baseSet","basePropertyDeep","basePullAll","basePullAt","indexes","previous","baseUnset","baseRange","step","baseRepeat","baseRest","setToString","overRest","baseSample","baseSampleSize","baseSetData","baseSetToString","constant","baseShuffle","baseSlice","baseSome","baseSortedIndex","retHighest","low","high","mid","baseSortedIndexBy","valIsNaN","valIsNull","valIsSymbol","valIsUndefined","othIsDefined","othIsNull","othIsReflexive","othIsSymbol","setLow","baseSortedUniq","baseToNumber","baseToString","baseUniq","createSet","seenIndex","baseUpdate","updater","baseWhile","isDrop","baseXor","baseZipObject","assignFunc","valsLength","castArrayLikeObject","castFunction","stringToPath","castRest","castSlice","copy","cloneArrayBuffer","arrayBuffer","byteLength","cloneDataView","dataView","byteOffset","cloneRegExp","regexp","cloneSymbol","symbol","typedArray","compareAscending","valIsDefined","valIsReflexive","objCriteria","othCriteria","ordersLength","order","composeArgs","partials","holders","isCurried","argsIndex","argsLength","holdersLength","leftIndex","leftLength","rangeLength","isUncurried","composeArgsRight","holdersIndex","rightIndex","rightLength","isNew","getSymbols","getSymbolsIn","createAggregator","initializer","createAssigner","assigner","sources","guard","isIterateeCall","iterable","createBind","isBind","createCtor","wrapper","createCaseFirst","methodName","trailing","createCompounder","words","deburr","thisBinding","createCurry","arity","getHolder","createRecurry","createHybrid","createFind","findIndexFunc","createFlow","flatRest","funcs","prereq","thru","getFuncName","funcName","isLaziable","plant","partialsRight","holdersRight","argPos","ary","isAry","isBindKey","isFlip","holdersCount","newHolders","reorder","createInverter","toIteratee","createMathOperation","operator","defaultValue","createOver","arrayFunc","createPadding","chars","charsLength","createPartial","createRange","toFinite","createRelationalOperation","wrapFunc","isCurry","newHoldersRight","newPartials","newPartialsRight","newData","setData","setWrapToString","createRound","precision","createToPairs","createWrap","customDefaultsAssignIn","customDefaultsMerge","customOmitClone","isPartial","arrValue","othValue","compared","convert","objProps","objLength","othProps","skipCtor","objCtor","othCtor","flatten","otherFunc","isKeyable","isOwn","unmasked","stubArray","transforms","getWrapDetails","hasPath","hasFunc","input","insertWrapDetails","details","ArrayBuffer","ctorString","isMaskable","stubFalse","memoizeCapped","memoize","srcBitmask","newBitmask","isCombo","otherArgs","oldArray","shortOut","reference","updateWrapDetails","lastCalled","stamp","remaining","rand","subString","chunk","compact","difference","differenceBy","differenceWith","drop","dropRight","dropRightWhile","dropWhile","fill","findLastIndex","flattenDeep","flattenDepth","fromPairs","intersection","mapped","intersectionBy","intersectionWith","separator","lastIndexOf","nth","pull","pullAll","pullAllBy","pullAllWith","pullAt","sortedIndex","sortedIndexBy","sortedIndexOf","sortedLastIndex","sortedLastIndexBy","sortedLastIndexOf","sortedUniq","sortedUniqBy","tail","take","takeRight","takeRightWhile","takeWhile","union","unionBy","unionWith","uniq","uniqBy","uniqWith","unzip","group","unzipWith","without","xor","xorBy","xorWith","zip","zipObject","zipObjectDeep","zipWith","chain","tap","interceptor","wrapperAt","wrapperChain","wrapperCommit","wrapperNext","wrapperToIterator","wrapperPlant","wrapperReverse","wrapped","wrapperValue","countBy","find","findLast","flatMap","flatMapDeep","flatMapDepth","forEachRight","groupBy","isString","invokeMap","keyBy","orderBy","partition","reduceRight","negate","sample","sampleSize","shuffle","sortBy","after","before","bindKey","curry","curryRight","debounce","lastArgs","lastThis","maxWait","timerId","lastCallTime","lastInvokeTime","leading","maxing","invokeFunc","time","leadingEdge","timerExpired","remainingWait","timeSinceLastCall","timeSinceLastInvoke","timeWaiting","shouldInvoke","trailingEdge","cancel","flush","debounced","isInvoking","flip","resolver","memoized","Cache","overArgs","funcsLength","partial","partialRight","rearg","rest","spread","throttle","unary","wrap","castArray","cloneWith","cloneDeep","cloneDeepWith","conformsTo","gte","isBoolean","isElement","isEmpty","isEqual","isEqualWith","isError","isInteger","isMatch","isMatchWith","isNumber","isNull","isNil","isSafeInteger","isUndefined","isWeakMap","isWeakSet","lte","sign","remainder","isBinary","toSafeInteger","assignIn","assignInWith","assignWith","at","properties","propsIndex","propsLength","defaultsDeep","mergeWith","findKey","findLastKey","forIn","forInRight","forOwn","forOwnRight","functions","functionsIn","invert","invertBy","mapKeys","mapValues","omit","omitBy","pickBy","setWith","toPairs","toPairsIn","isArrLike","unset","updateWith","valuesIn","clamp","inRange","floating","temp","camelCase","word","upperFirst","endsWith","position","escapeRegExp","kebabCase","lowerCase","lowerFirst","pad","strLength","padEnd","padStart","radix","repeat","snakeCase","limit","startCase","startsWith","template","settings","isEscaping","isEvaluating","importsKeys","importsValues","reDelimiters","sourceURL","escapeValue","interpolateValue","esTemplateValue","evaluateValue","attempt","toLower","toUpper","trimEnd","trimStart","truncate","omission","search","newEnd","upperCase","bindAll","methodNames","cond","conforms","defaultTo","flow","flowRight","matchesProperty","methodOf","noConflict","nthArg","over","overEvery","overSome","propertyOf","range","rangeRight","stubObject","stubString","stubTrue","times","toPath","uniqueId","prefix","augend","addend","divide","dividend","divisor","maxBy","mean","meanBy","minBy","multiply","multiplier","multiplicand","round","subtract","minuend","subtrahend","sum","sumBy","entriesIn","extendWith","each","eachRight","first","isFilter","takeName","dropName","checkIteratee","isTaker","lodashFunc","retUnwrapped","isLazy","useLazy","isHybrid","isUnwrapped","onlyLazy","chainName","names","toJSON","fails","ArrayProto","un","createDesc","ObjectProto","descriptor","bitmap","isArrayIter","getIterFn","BREAK","RETURN","iterFn","ie","with_query_strings","mockIE","MATCH","re","Internal","newGenericPromiseCapability","OwnPromiseCapability","Wrapper","anInstance","forOf","speciesConstructor","microtask","newPromiseCapabilityModule","perform","promiseResolve","PROMISE","versions","v8","$Promise","isNode","empty","newPromiseCapability","USE_NATIVE","promise","FakePromise","PromiseRejectionEvent","isThenable","isReject","reaction","exited","fail","domain","_h","onHandleUnhandled","exit","onUnhandled","unhandled","isUnhandled","onunhandledrejection","_a","onrejectionhandled","$reject","_d","_w","$resolve","executor","onFulfilled","onRejected","G","W","r","capability","$$reject","x","iter","$index","alreadyCalled","race","core","SHARED","copyright","$sort","comparefn","own","exp","IS_FORCED","IS_GLOBAL","IS_STATIC","IS_PROTO","IS_BIND","expProto","U","R","SAFE_CLOSING","riter","skipClosing","shared","webpackPolyfill","deprecate","$includes","$flags","DESCRIPTORS","define","_callbacks","off","removeListener","removeAllListeners","gOPS","pIE","$assign","A","K","k","aLen","isEnum","__g","FProto","nameRE","stat","__e","macrotask","MutationObserver","WebKitMutationObserver","standalone","toggle","characterData","documentMode","IE8_DOM_DEFINE","toPrimitive","Attributes","_arrayWithoutHoles","arr2","_iterableToArray","_nonIterableSpread","_toConsumableArray","condition","View","routerView","route","$route","_routerViewCache","inactive","_routerRoot","routerViewDepth","matched","registerRouteInstance","instances","propsToPass","resolveProps","encodeReserveRE","encodeReserveReplacer","commaRE","decode","resolveQuery","extraQuery","_parseQuery","parsedQuery","parseQuery","param","parts","stringifyQuery","val2","trailingSlashRE","createRoute","record","redirectedFrom","router","stringifyQuery$$1","meta","fullPath","getFullPath","formatMatch","START","_stringifyQuery","isSameRoute","isObjectEqual","aKeys","bKeys","aVal","bVal","isIncludedRoute","queryIncludes","toTypes","eventTypes","Link","required","exact","exactActiveClass","$router","classes","globalActiveClass","linkActiveClass","globalExactActiveClass","linkExactActiveClass","activeClassFallback","exactActiveClassFallback","compareTarget","guardEvent","click","findAnchor","aData","aAttrs","metaKey","ctrlKey","shiftKey","defaultPrevented","button","currentTarget","preventDefault","installed","registerInstance","callVal","_router","history","_route","beforeRouteEnter","beforeRouteLeave","beforeRouteUpdate","resolvePath","relative","firstChar","segment","hashIndex","queryIndex","cleanPath","isarray","pathToRegexp_1","pathToRegexp","parse_1","compile_1","compile","tokensToFunction_1","tokensToFunction","tokensToRegExp_1","tokensToRegExp","PATH_REGEXP","defaultDelimiter","delimiter","m","modifier","asterisk","optional","escapeGroup","escapeString","substr","encodeURIComponentPretty","encodeAsterisk","pretty","attachKeys","sensitive","regexpToRegexp","groups","arrayToRegexp","stringToRegexp","endsWithDelimiter","regexpCompileCache","fillParams","routeMsg","filler","createRouteMap","routes","oldPathList","oldPathMap","oldNameMap","pathList","pathMap","nameMap","addRouteRecord","matchAs","pathToRegexpOptions","normalizedPath","normalizePath","caseSensitive","compileRouteRegex","childMatchAs","alias","aliases","aliasRoute","normalizeLocation","_normalized","rawPath","parsedPath","basePath","createMatcher","addRoutes","currentRoute","_createRoute","paramNames","record$1","matchRoute","originalRedirect","resolveRecordPath","resolvedPath","aliasedPath","aliasedMatch","aliasedRecord","positionStore","setupScroll","getStateKey","saveScrollPosition","setStateKey","handleScroll","isPop","app","behavior","scrollBehavior","getScrollPosition","shouldScroll","scrollToPosition","pageXOffset","y","pageYOffset","getElementPosition","docEl","docRect","elRect","isValidPosition","normalizePosition","normalizeOffset","selector","scrollTo","supportsPushState","ua","Time","_key","genKey","toFixed","pushState","runQueue","resolveAsyncComponents","hasAsync","flatMapComponents","resolvedDef","isESModule","History","normalizeBase","ready","readyCbs","readyErrorCbs","errorCbs","baseEl","resolveQueue","activated","deactivated","extractGuards","records","guards","instance","extractGuard","extractLeaveGuards","bindGuard","extractUpdateHooks","extractEnterGuards","isValid","bindEnterGuard","poll","listen","onReady","errorCb","onError","transitionTo","onComplete","onAbort","confirmTransition","updateRoute","ensureURL","abort","beforeHooks","postEnterCbs","enterGuards","resolveHooks","prev","afterHooks","HTML5History","History$$1","expectScroll","initLocation","getLocation","go","fromRoute","getCurrentLocation","pathname","HashHistory","checkFallback","ensureSlash","setupListeners","supportsScroll","getHash","replaceHash","pushHash","getUrl","AbstractHistory","targetIndex","VueRouter","apps","matcher","registerHook","createHref","setupHashListener","beforeEach","beforeResolve","afterEach","back","forward","getMatchedComponents","normalizedTo","_arrayWithHoles","_iterableToArrayLimit","_arr","_nonIterableRest","_slicedToArray","_timer","_timeout","_responseTimeout","deadline","retry","_retryCallback","ERROR_CODES","override","_fullfilledPromise","innerResolve","innerReject","_okCallback","getHeader","base64Encoder","redirects","_maxRedirects","maxResponseSize","_maxResponseSize","isObj","sortQuery","_sort","queryArr","_appendQueryString","trace","_timeoutError","errno","UNSCOPABLES","_defaults","rel","cleanHeader","changesOrigin","_defineProperty","PromiseCapability","$$resolve","$iterators","wks","TO_STRING_TAG","ArrayValues","DOMIterables","CSSRuleList","CSSStyleDeclaration","CSSValueList","ClientRectList","DOMRectList","DOMStringList","DOMTokenList","DataTransferItemList","FileList","HTMLAllCollection","HTMLCollection","HTMLFormElement","HTMLSelectElement","MediaList","MimeTypeArray","NamedNodeMap","NodeList","PaintRequestList","Plugin","PluginArray","SVGLengthList","SVGNumberList","SVGPathSegList","SVGPointList","SVGStringList","SVGTransformList","SourceBufferList","StyleSheetList","TextTrackCueList","TextTrackList","TouchList","collections","explicit","Collection","promiseCapability","toIObject","toAbsoluteIndex","IS_INCLUDES","g","eval","_objectSpread","sym","_defineProperty__WEBPACK_IMPORTED_MODULE_0__","px","addToUnscopables","iterated","Arguments","arrayIndexOf"],"mappings":"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,IAAAC,IAAA,IAAAC,IAAA,IACAC,IAAA,IAAAC,IAAA,IAAAC,IAAA,IAAAC,IAAA,IACAC,IAAA,IAAAC,IAAA,IAAAC,IAAA,IAAAC,IAAA,IACAC,IAAA,IAAAC,IAAA,IAAAC,IAAA,IAAAC,IAAA,IACAC,IAAA,IAAAC,IAAA,IAAAC,IAAA,IAAAC,IAAA,IAAAC,IAAA,IACAC,IAAA,IAAAC,IAAA,IAAAC,IAAA,IAAAC,IAAA,IAAAC,IAAA,IACAC,IAAA,IAAAC,IAAA,IAAAC,IAAA,IAAAC,IAAA,IACAC,IAAA,IAAAC,IAAA,IAAAC,IAAA,IAAAC,IAAA,IACAC,IAAA,IAAAC,IAAA,IAAAC,IAAA,IAAAC,IAAA,IACAC,IAAA,IAAAC,IAAA,IAAAC,IAAA,IAAAC,IAAA,IAAAC,IAAA,IACAC,IAAA,IAAAC,IAAA,IAAAC,IAAA,IAAAC,IAAA,IAAAC,IAAA,IACAC,IAAA,IAAAC,IAAA,IACAC,IAAA,IAAAC,IAAA,IAAAC,IAAA,IACAC,IAAA,IAAAC,IAAA,IAAAC,IAAA,IAAAC,IAAA,IAAAC,IAAA,IACAC,IAAA,IAAAC,IAAA,IAAAC,IAAA,IAAAC,IAAA,IAAAC,IAAA,IACAC,IAAA,IAAAC,IAAA,IAAAC,IAAA,IAAAC,IAAA,IACAC,IAAA,IAAAC,IAAA,IAAAC,IAAA,IAAAC,IAAA,IACAC,IAAA,IAAAC,IAAA,IAAAC,IAAA,IACAC,IAAA,IAAAC,IAAA,IAAAC,IAAA,IACAC,IAAA,IAAAC,IAAA,IAAAC,IAAA,IACAC,IAAA,IAAAC,IAAA,IAAAC,IAAA,IACAC,IAAA,IAAAC,IAAA,IAAAC,IAAA,IAAAC,IAAA,IACAC,IAAA,IAAAC,IAAA,IAAAC,IAAA,IAAAC,IAAA,IACAC,IAAA,IAAAC,IAAA,IAAAC,IAAA,IACAC,IAAA,IAAAC,IAAA,IAAAC,IAAA,IACAC,IAAA,IAAAC,IAAA,IAAAC,IAAA,IAAAC,IAAA,IAAAC,IAAA,IAAAC,IAAA,IACAC,IAAA,IAAAC,IAAA,IAAAC,IAAA,IAAAC,IAAA,IAAAC,IAAA,IAAAC,IAAA,IACAC,IAAA,IAAAC,IAAA,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","file":"chunk-vendors.b9a11975.js","sourcesContent":["var document = require('./_global').document;\nmodule.exports = document && document.documentElement;\n","'use strict';\n\n/**\n * Module dependencies.\n */\n\nvar utils = require('./utils');\n\n/**\n * Expose `ResponseBase`.\n */\n\nmodule.exports = ResponseBase;\n\n/**\n * Initialize a new `ResponseBase`.\n *\n * @api public\n */\n\nfunction ResponseBase(obj) {\n if (obj) return mixin(obj);\n}\n\n/**\n * Mixin the prototype properties.\n *\n * @param {Object} obj\n * @return {Object}\n * @api private\n */\n\nfunction mixin(obj) {\n for (var key in ResponseBase.prototype) {\n obj[key] = ResponseBase.prototype[key];\n }\n return obj;\n}\n\n/**\n * Get case-insensitive `field` value.\n *\n * @param {String} field\n * @return {String}\n * @api public\n */\n\nResponseBase.prototype.get = function(field) {\n return this.header[field.toLowerCase()];\n};\n\n/**\n * Set header related properties:\n *\n * - `.type` the content type without params\n *\n * A response of \"Content-Type: text/plain; charset=utf-8\"\n * will provide you with a `.type` of \"text/plain\".\n *\n * @param {Object} header\n * @api private\n */\n\nResponseBase.prototype._setHeaderProperties = function(header){\n // TODO: moar!\n // TODO: make this a util\n\n // content-type\n var ct = header['content-type'] || '';\n this.type = utils.type(ct);\n\n // params\n var params = utils.params(ct);\n for (var key in params) this[key] = params[key];\n\n this.links = {};\n\n // links\n try {\n if (header.link) {\n this.links = utils.parseLinks(header.link);\n }\n } catch (err) {\n // ignore\n }\n};\n\n/**\n * Set flags such as `.ok` based on `status`.\n *\n * For example a 2xx response will give you a `.ok` of __true__\n * whereas 5xx will be __false__ and `.error` will be __true__. The\n * `.clientError` and `.serverError` are also available to be more\n * specific, and `.statusType` is the class of error ranging from 1..5\n * sometimes useful for mapping respond colors etc.\n *\n * \"sugar\" properties are also defined for common cases. Currently providing:\n *\n * - .noContent\n * - .badRequest\n * - .unauthorized\n * - .notAcceptable\n * - .notFound\n *\n * @param {Number} status\n * @api private\n */\n\nResponseBase.prototype._setStatusProperties = function(status){\n var type = status / 100 | 0;\n\n // status / class\n this.status = this.statusCode = status;\n this.statusType = type;\n\n // basics\n this.info = 1 == type;\n this.ok = 2 == type;\n this.redirect = 3 == type;\n this.clientError = 4 == type;\n this.serverError = 5 == type;\n this.error = (4 == type || 5 == type)\n ? this.toError()\n : false;\n\n // sugar\n this.created = 201 == status;\n this.accepted = 202 == status;\n this.noContent = 204 == status;\n this.badRequest = 400 == status;\n this.unauthorized = 401 == status;\n this.notAcceptable = 406 == status;\n this.forbidden = 403 == status;\n this.notFound = 404 == status;\n this.unprocessableEntity = 422 == status;\n};\n","module.exports = function (it) {\n return typeof it === 'object' ? it !== null : typeof it === 'function';\n};\n","// helper for String#{startsWith, endsWith, includes}\nvar isRegExp = require('./_is-regexp');\nvar defined = require('./_defined');\n\nmodule.exports = function (that, searchString, NAME) {\n if (isRegExp(searchString)) throw TypeError('String#' + NAME + \" doesn't accept regex!\");\n return String(defined(that));\n};\n","module.exports = function (done, value) {\n return { value: value, done: !!done };\n};\n","/**\n * Root reference for iframes.\n */\n\nvar root;\nif (typeof window !== 'undefined') { // Browser window\n root = window;\n} else if (typeof self !== 'undefined') { // Web Worker\n root = self;\n} else { // Other environments\n console.warn(\"Using browser-only version of superagent in non-browser environment\");\n root = this;\n}\n\nvar Emitter = require('component-emitter');\nvar RequestBase = require('./request-base');\nvar isObject = require('./is-object');\nvar ResponseBase = require('./response-base');\nvar Agent = require('./agent-base');\n\n/**\n * Noop.\n */\n\nfunction noop(){};\n\n/**\n * Expose `request`.\n */\n\nvar request = exports = module.exports = function(method, url) {\n // callback\n if ('function' == typeof url) {\n return new exports.Request('GET', method).end(url);\n }\n\n // url first\n if (1 == arguments.length) {\n return new exports.Request('GET', method);\n }\n\n return new exports.Request(method, url);\n}\n\nexports.Request = Request;\n\n/**\n * Determine XHR.\n */\n\nrequest.getXHR = function () {\n if (root.XMLHttpRequest\n && (!root.location || 'file:' != root.location.protocol\n || !root.ActiveXObject)) {\n return new XMLHttpRequest;\n } else {\n try { return new ActiveXObject('Microsoft.XMLHTTP'); } catch(e) {}\n try { return new ActiveXObject('Msxml2.XMLHTTP.6.0'); } catch(e) {}\n try { return new ActiveXObject('Msxml2.XMLHTTP.3.0'); } catch(e) {}\n try { return new ActiveXObject('Msxml2.XMLHTTP'); } catch(e) {}\n }\n throw Error(\"Browser-only version of superagent could not find XHR\");\n};\n\n/**\n * Removes leading and trailing whitespace, added to support IE.\n *\n * @param {String} s\n * @return {String}\n * @api private\n */\n\nvar trim = ''.trim\n ? function(s) { return s.trim(); }\n : function(s) { return s.replace(/(^\\s*|\\s*$)/g, ''); };\n\n/**\n * Serialize the given `obj`.\n *\n * @param {Object} obj\n * @return {String}\n * @api private\n */\n\nfunction serialize(obj) {\n if (!isObject(obj)) return obj;\n var pairs = [];\n for (var key in obj) {\n pushEncodedKeyValuePair(pairs, key, obj[key]);\n }\n return pairs.join('&');\n}\n\n/**\n * Helps 'serialize' with serializing arrays.\n * Mutates the pairs array.\n *\n * @param {Array} pairs\n * @param {String} key\n * @param {Mixed} val\n */\n\nfunction pushEncodedKeyValuePair(pairs, key, val) {\n if (val != null) {\n if (Array.isArray(val)) {\n val.forEach(function(v) {\n pushEncodedKeyValuePair(pairs, key, v);\n });\n } else if (isObject(val)) {\n for(var subkey in val) {\n pushEncodedKeyValuePair(pairs, key + '[' + subkey + ']', val[subkey]);\n }\n } else {\n pairs.push(encodeURIComponent(key)\n + '=' + encodeURIComponent(val));\n }\n } else if (val === null) {\n pairs.push(encodeURIComponent(key));\n }\n}\n\n/**\n * Expose serialization method.\n */\n\nrequest.serializeObject = serialize;\n\n/**\n * Parse the given x-www-form-urlencoded `str`.\n *\n * @param {String} str\n * @return {Object}\n * @api private\n */\n\nfunction parseString(str) {\n var obj = {};\n var pairs = str.split('&');\n var pair;\n var pos;\n\n for (var i = 0, len = pairs.length; i < len; ++i) {\n pair = pairs[i];\n pos = pair.indexOf('=');\n if (pos == -1) {\n obj[decodeURIComponent(pair)] = '';\n } else {\n obj[decodeURIComponent(pair.slice(0, pos))] =\n decodeURIComponent(pair.slice(pos + 1));\n }\n }\n\n return obj;\n}\n\n/**\n * Expose parser.\n */\n\nrequest.parseString = parseString;\n\n/**\n * Default MIME type map.\n *\n * superagent.types.xml = 'application/xml';\n *\n */\n\nrequest.types = {\n html: 'text/html',\n json: 'application/json',\n xml: 'text/xml',\n urlencoded: 'application/x-www-form-urlencoded',\n 'form': 'application/x-www-form-urlencoded',\n 'form-data': 'application/x-www-form-urlencoded'\n};\n\n/**\n * Default serialization map.\n *\n * superagent.serialize['application/xml'] = function(obj){\n * return 'generated xml here';\n * };\n *\n */\n\nrequest.serialize = {\n 'application/x-www-form-urlencoded': serialize,\n 'application/json': JSON.stringify\n};\n\n/**\n * Default parsers.\n *\n * superagent.parse['application/xml'] = function(str){\n * return { object parsed from str };\n * };\n *\n */\n\nrequest.parse = {\n 'application/x-www-form-urlencoded': parseString,\n 'application/json': JSON.parse\n};\n\n/**\n * Parse the given header `str` into\n * an object containing the mapped fields.\n *\n * @param {String} str\n * @return {Object}\n * @api private\n */\n\nfunction parseHeader(str) {\n var lines = str.split(/\\r?\\n/);\n var fields = {};\n var index;\n var line;\n var field;\n var val;\n\n for (var i = 0, len = lines.length; i < len; ++i) {\n line = lines[i];\n index = line.indexOf(':');\n if (index === -1) { // could be empty line, just skip it\n continue;\n }\n field = line.slice(0, index).toLowerCase();\n val = trim(line.slice(index + 1));\n fields[field] = val;\n }\n\n return fields;\n}\n\n/**\n * Check if `mime` is json or has +json structured syntax suffix.\n *\n * @param {String} mime\n * @return {Boolean}\n * @api private\n */\n\nfunction isJSON(mime) {\n // should match /json or +json\n // but not /json-seq\n return /[\\/+]json($|[^-\\w])/.test(mime);\n}\n\n/**\n * Initialize a new `Response` with the given `xhr`.\n *\n * - set flags (.ok, .error, etc)\n * - parse header\n *\n * Examples:\n *\n * Aliasing `superagent` as `request` is nice:\n *\n * request = superagent;\n *\n * We can use the promise-like API, or pass callbacks:\n *\n * request.get('/').end(function(res){});\n * request.get('/', function(res){});\n *\n * Sending data can be chained:\n *\n * request\n * .post('/user')\n * .send({ name: 'tj' })\n * .end(function(res){});\n *\n * Or passed to `.send()`:\n *\n * request\n * .post('/user')\n * .send({ name: 'tj' }, function(res){});\n *\n * Or passed to `.post()`:\n *\n * request\n * .post('/user', { name: 'tj' })\n * .end(function(res){});\n *\n * Or further reduced to a single call for simple cases:\n *\n * request\n * .post('/user', { name: 'tj' }, function(res){});\n *\n * @param {XMLHTTPRequest} xhr\n * @param {Object} options\n * @api private\n */\n\nfunction Response(req) {\n this.req = req;\n this.xhr = this.req.xhr;\n // responseText is accessible only if responseType is '' or 'text' and on older browsers\n this.text = ((this.req.method !='HEAD' && (this.xhr.responseType === '' || this.xhr.responseType === 'text')) || typeof this.xhr.responseType === 'undefined')\n ? this.xhr.responseText\n : null;\n this.statusText = this.req.xhr.statusText;\n var status = this.xhr.status;\n // handle IE9 bug: http://stackoverflow.com/questions/10046972/msie-returns-status-code-of-1223-for-ajax-request\n if (status === 1223) {\n status = 204;\n }\n this._setStatusProperties(status);\n this.header = this.headers = parseHeader(this.xhr.getAllResponseHeaders());\n // getAllResponseHeaders sometimes falsely returns \"\" for CORS requests, but\n // getResponseHeader still works. so we get content-type even if getting\n // other headers fails.\n this.header['content-type'] = this.xhr.getResponseHeader('content-type');\n this._setHeaderProperties(this.header);\n\n if (null === this.text && req._responseType) {\n this.body = this.xhr.response;\n } else {\n this.body = this.req.method != 'HEAD'\n ? this._parseBody(this.text ? this.text : this.xhr.response)\n : null;\n }\n}\n\nResponseBase(Response.prototype);\n\n/**\n * Parse the given body `str`.\n *\n * Used for auto-parsing of bodies. Parsers\n * are defined on the `superagent.parse` object.\n *\n * @param {String} str\n * @return {Mixed}\n * @api private\n */\n\nResponse.prototype._parseBody = function(str) {\n var parse = request.parse[this.type];\n if (this.req._parser) {\n return this.req._parser(this, str);\n }\n if (!parse && isJSON(this.type)) {\n parse = request.parse['application/json'];\n }\n return parse && str && (str.length || str instanceof Object)\n ? parse(str)\n : null;\n};\n\n/**\n * Return an `Error` representative of this response.\n *\n * @return {Error}\n * @api public\n */\n\nResponse.prototype.toError = function(){\n var req = this.req;\n var method = req.method;\n var url = req.url;\n\n var msg = 'cannot ' + method + ' ' + url + ' (' + this.status + ')';\n var err = new Error(msg);\n err.status = this.status;\n err.method = method;\n err.url = url;\n\n return err;\n};\n\n/**\n * Expose `Response`.\n */\n\nrequest.Response = Response;\n\n/**\n * Initialize a new `Request` with the given `method` and `url`.\n *\n * @param {String} method\n * @param {String} url\n * @api public\n */\n\nfunction Request(method, url) {\n var self = this;\n this._query = this._query || [];\n this.method = method;\n this.url = url;\n this.header = {}; // preserves header name case\n this._header = {}; // coerces header names to lowercase\n this.on('end', function(){\n var err = null;\n var res = null;\n\n try {\n res = new Response(self);\n } catch(e) {\n err = new Error('Parser is unable to parse the response');\n err.parse = true;\n err.original = e;\n // issue #675: return the raw response if the response parsing fails\n if (self.xhr) {\n // ie9 doesn't have 'response' property\n err.rawResponse = typeof self.xhr.responseType == 'undefined' ? self.xhr.responseText : self.xhr.response;\n // issue #876: return the http status code if the response parsing fails\n err.status = self.xhr.status ? self.xhr.status : null;\n err.statusCode = err.status; // backwards-compat only\n } else {\n err.rawResponse = null;\n err.status = null;\n }\n\n return self.callback(err);\n }\n\n self.emit('response', res);\n\n var new_err;\n try {\n if (!self._isResponseOK(res)) {\n new_err = new Error(res.statusText || 'Unsuccessful HTTP response');\n }\n } catch(custom_err) {\n new_err = custom_err; // ok() callback can throw\n }\n\n // #1000 don't catch errors from the callback to avoid double calling it\n if (new_err) {\n new_err.original = err;\n new_err.response = res;\n new_err.status = res.status;\n self.callback(new_err, res);\n } else {\n self.callback(null, res);\n }\n });\n}\n\n/**\n * Mixin `Emitter` and `RequestBase`.\n */\n\nEmitter(Request.prototype);\nRequestBase(Request.prototype);\n\n/**\n * Set Content-Type to `type`, mapping values from `request.types`.\n *\n * Examples:\n *\n * superagent.types.xml = 'application/xml';\n *\n * request.post('/')\n * .type('xml')\n * .send(xmlstring)\n * .end(callback);\n *\n * request.post('/')\n * .type('application/xml')\n * .send(xmlstring)\n * .end(callback);\n *\n * @param {String} type\n * @return {Request} for chaining\n * @api public\n */\n\nRequest.prototype.type = function(type){\n this.set('Content-Type', request.types[type] || type);\n return this;\n};\n\n/**\n * Set Accept to `type`, mapping values from `request.types`.\n *\n * Examples:\n *\n * superagent.types.json = 'application/json';\n *\n * request.get('/agent')\n * .accept('json')\n * .end(callback);\n *\n * request.get('/agent')\n * .accept('application/json')\n * .end(callback);\n *\n * @param {String} accept\n * @return {Request} for chaining\n * @api public\n */\n\nRequest.prototype.accept = function(type){\n this.set('Accept', request.types[type] || type);\n return this;\n};\n\n/**\n * Set Authorization field value with `user` and `pass`.\n *\n * @param {String} user\n * @param {String} [pass] optional in case of using 'bearer' as type\n * @param {Object} options with 'type' property 'auto', 'basic' or 'bearer' (default 'basic')\n * @return {Request} for chaining\n * @api public\n */\n\nRequest.prototype.auth = function(user, pass, options){\n if (1 === arguments.length) pass = '';\n if (typeof pass === 'object' && pass !== null) { // pass is optional and can be replaced with options\n options = pass;\n pass = '';\n }\n if (!options) {\n options = {\n type: 'function' === typeof btoa ? 'basic' : 'auto',\n };\n }\n\n var encoder = function(string) {\n if ('function' === typeof btoa) {\n return btoa(string);\n }\n throw new Error('Cannot use basic auth, btoa is not a function');\n };\n\n return this._auth(user, pass, options, encoder);\n};\n\n/**\n * Add query-string `val`.\n *\n * Examples:\n *\n * request.get('/shoes')\n * .query('size=10')\n * .query({ color: 'blue' })\n *\n * @param {Object|String} val\n * @return {Request} for chaining\n * @api public\n */\n\nRequest.prototype.query = function(val){\n if ('string' != typeof val) val = serialize(val);\n if (val) this._query.push(val);\n return this;\n};\n\n/**\n * Queue the given `file` as an attachment to the specified `field`,\n * with optional `options` (or filename).\n *\n * ``` js\n * request.post('/upload')\n * .attach('content', new Blob(['
    hey!'], { type: \"text/html\"}))\n * .end(callback);\n * ```\n *\n * @param {String} field\n * @param {Blob|File} file\n * @param {String|Object} options\n * @return {Request} for chaining\n * @api public\n */\n\nRequest.prototype.attach = function(field, file, options){\n if (file) {\n if (this._data) {\n throw Error(\"superagent can't mix .send() and .attach()\");\n }\n\n this._getFormData().append(field, file, options || file.name);\n }\n return this;\n};\n\nRequest.prototype._getFormData = function(){\n if (!this._formData) {\n this._formData = new root.FormData();\n }\n return this._formData;\n};\n\n/**\n * Invoke the callback with `err` and `res`\n * and handle arity check.\n *\n * @param {Error} err\n * @param {Response} res\n * @api private\n */\n\nRequest.prototype.callback = function(err, res){\n if (this._shouldRetry(err, res)) {\n return this._retry();\n }\n\n var fn = this._callback;\n this.clearTimeout();\n\n if (err) {\n if (this._maxRetries) err.retries = this._retries - 1;\n this.emit('error', err);\n }\n\n fn(err, res);\n};\n\n/**\n * Invoke callback with x-domain error.\n *\n * @api private\n */\n\nRequest.prototype.crossDomainError = function(){\n var err = new Error('Request has been terminated\\nPossible causes: the network is offline, Origin is not allowed by Access-Control-Allow-Origin, the page is being unloaded, etc.');\n err.crossDomain = true;\n\n err.status = this.status;\n err.method = this.method;\n err.url = this.url;\n\n this.callback(err);\n};\n\n// This only warns, because the request is still likely to work\nRequest.prototype.buffer = Request.prototype.ca = Request.prototype.agent = function(){\n console.warn(\"This is not supported in browser version of superagent\");\n return this;\n};\n\n// This throws, because it can't send/receive data as expected\nRequest.prototype.pipe = Request.prototype.write = function(){\n throw Error(\"Streaming is not supported in browser version of superagent\");\n};\n\n/**\n * Check if `obj` is a host object,\n * we don't want to serialize these :)\n *\n * @param {Object} obj\n * @return {Boolean}\n * @api private\n */\nRequest.prototype._isHost = function _isHost(obj) {\n // Native objects stringify to [object File], [object Blob], [object FormData], etc.\n return obj && 'object' === typeof obj && !Array.isArray(obj) && Object.prototype.toString.call(obj) !== '[object Object]';\n}\n\n/**\n * Initiate request, invoking callback `fn(res)`\n * with an instanceof `Response`.\n *\n * @param {Function} fn\n * @return {Request} for chaining\n * @api public\n */\n\nRequest.prototype.end = function(fn){\n if (this._endCalled) {\n console.warn(\"Warning: .end() was called twice. This is not supported in superagent\");\n }\n this._endCalled = true;\n\n // store callback\n this._callback = fn || noop;\n\n // querystring\n this._finalizeQueryString();\n\n return this._end();\n};\n\nRequest.prototype._end = function() {\n var self = this;\n var xhr = (this.xhr = request.getXHR());\n var data = this._formData || this._data;\n\n this._setTimeouts();\n\n // state change\n xhr.onreadystatechange = function(){\n var readyState = xhr.readyState;\n if (readyState >= 2 && self._responseTimeoutTimer) {\n clearTimeout(self._responseTimeoutTimer);\n }\n if (4 != readyState) {\n return;\n }\n\n // In IE9, reads to any property (e.g. status) off of an aborted XHR will\n // result in the error \"Could not complete the operation due to error c00c023f\"\n var status;\n try { status = xhr.status } catch(e) { status = 0; }\n\n if (!status) {\n if (self.timedout || self._aborted) return;\n return self.crossDomainError();\n }\n self.emit('end');\n };\n\n // progress\n var handleProgress = function(direction, e) {\n if (e.total > 0) {\n e.percent = e.loaded / e.total * 100;\n }\n e.direction = direction;\n self.emit('progress', e);\n };\n if (this.hasListeners('progress')) {\n try {\n xhr.onprogress = handleProgress.bind(null, 'download');\n if (xhr.upload) {\n xhr.upload.onprogress = handleProgress.bind(null, 'upload');\n }\n } catch(e) {\n // Accessing xhr.upload fails in IE from a web worker, so just pretend it doesn't exist.\n // Reported here:\n // https://connect.microsoft.com/IE/feedback/details/837245/xmlhttprequest-upload-throws-invalid-argument-when-used-from-web-worker-context\n }\n }\n\n // initiate request\n try {\n if (this.username && this.password) {\n xhr.open(this.method, this.url, true, this.username, this.password);\n } else {\n xhr.open(this.method, this.url, true);\n }\n } catch (err) {\n // see #1149\n return this.callback(err);\n }\n\n // CORS\n if (this._withCredentials) xhr.withCredentials = true;\n\n // body\n if (!this._formData && 'GET' != this.method && 'HEAD' != this.method && 'string' != typeof data && !this._isHost(data)) {\n // serialize stuff\n var contentType = this._header['content-type'];\n var serialize = this._serializer || request.serialize[contentType ? contentType.split(';')[0] : ''];\n if (!serialize && isJSON(contentType)) {\n serialize = request.serialize['application/json'];\n }\n if (serialize) data = serialize(data);\n }\n\n // set header fields\n for (var field in this.header) {\n if (null == this.header[field]) continue;\n\n if (this.header.hasOwnProperty(field))\n xhr.setRequestHeader(field, this.header[field]);\n }\n\n if (this._responseType) {\n xhr.responseType = this._responseType;\n }\n\n // send stuff\n this.emit('request', this);\n\n // IE11 xhr.send(undefined) sends 'undefined' string as POST payload (instead of nothing)\n // We need null here if data is undefined\n xhr.send(typeof data !== 'undefined' ? data : null);\n return this;\n};\n\nrequest.agent = function() {\n return new Agent();\n};\n\n[\"GET\", \"POST\", \"OPTIONS\", \"PATCH\", \"PUT\", \"DELETE\"].forEach(function(method) {\n Agent.prototype[method.toLowerCase()] = function(url, fn) {\n var req = new request.Request(method, url);\n this._setDefaults(req);\n if (fn) {\n req.end(fn);\n }\n return req;\n };\n});\n\nAgent.prototype.del = Agent.prototype['delete'];\n\n/**\n * GET `url` with optional callback `fn(res)`.\n *\n * @param {String} url\n * @param {Mixed|Function} [data] or fn\n * @param {Function} [fn]\n * @return {Request}\n * @api public\n */\n\nrequest.get = function(url, data, fn) {\n var req = request('GET', url);\n if ('function' == typeof data) (fn = data), (data = null);\n if (data) req.query(data);\n if (fn) req.end(fn);\n return req;\n};\n\n/**\n * HEAD `url` with optional callback `fn(res)`.\n *\n * @param {String} url\n * @param {Mixed|Function} [data] or fn\n * @param {Function} [fn]\n * @return {Request}\n * @api public\n */\n\nrequest.head = function(url, data, fn) {\n var req = request('HEAD', url);\n if ('function' == typeof data) (fn = data), (data = null);\n if (data) req.query(data);\n if (fn) req.end(fn);\n return req;\n};\n\n/**\n * OPTIONS query to `url` with optional callback `fn(res)`.\n *\n * @param {String} url\n * @param {Mixed|Function} [data] or fn\n * @param {Function} [fn]\n * @return {Request}\n * @api public\n */\n\nrequest.options = function(url, data, fn) {\n var req = request('OPTIONS', url);\n if ('function' == typeof data) (fn = data), (data = null);\n if (data) req.send(data);\n if (fn) req.end(fn);\n return req;\n};\n\n/**\n * DELETE `url` with optional `data` and callback `fn(res)`.\n *\n * @param {String} url\n * @param {Mixed} [data]\n * @param {Function} [fn]\n * @return {Request}\n * @api public\n */\n\nfunction del(url, data, fn) {\n var req = request('DELETE', url);\n if ('function' == typeof data) (fn = data), (data = null);\n if (data) req.send(data);\n if (fn) req.end(fn);\n return req;\n}\n\nrequest['del'] = del;\nrequest['delete'] = del;\n\n/**\n * PATCH `url` with optional `data` and callback `fn(res)`.\n *\n * @param {String} url\n * @param {Mixed} [data]\n * @param {Function} [fn]\n * @return {Request}\n * @api public\n */\n\nrequest.patch = function(url, data, fn) {\n var req = request('PATCH', url);\n if ('function' == typeof data) (fn = data), (data = null);\n if (data) req.send(data);\n if (fn) req.end(fn);\n return req;\n};\n\n/**\n * POST `url` with optional `data` and callback `fn(res)`.\n *\n * @param {String} url\n * @param {Mixed} [data]\n * @param {Function} [fn]\n * @return {Request}\n * @api public\n */\n\nrequest.post = function(url, data, fn) {\n var req = request('POST', url);\n if ('function' == typeof data) (fn = data), (data = null);\n if (data) req.send(data);\n if (fn) req.end(fn);\n return req;\n};\n\n/**\n * PUT `url` with optional `data` and callback `fn(res)`.\n *\n * @param {String} url\n * @param {Mixed|Function} [data] or fn\n * @param {Function} [fn]\n * @return {Request}\n * @api public\n */\n\nrequest.put = function(url, data, fn) {\n var req = request('PUT', url);\n if ('function' == typeof data) (fn = data), (data = null);\n if (data) req.send(data);\n if (fn) req.end(fn);\n return req;\n};\n","module.exports = function (it) {\n if (typeof it != 'function') throw TypeError(it + ' is not a function!');\n return it;\n};\n","var redefine = require('./_redefine');\nmodule.exports = function (target, src, safe) {\n for (var key in src) redefine(target, key, src[key], safe);\n return target;\n};\n","// IE 8- don't enum bug keys\nmodule.exports = (\n 'constructor,hasOwnProperty,isPrototypeOf,propertyIsEnumerable,toLocaleString,toString,valueOf'\n).split(',');\n","// 7.3.20 SpeciesConstructor(O, defaultConstructor)\nvar anObject = require('./_an-object');\nvar aFunction = require('./_a-function');\nvar SPECIES = require('./_wks')('species');\nmodule.exports = function (O, D) {\n var C = anObject(O).constructor;\n var S;\n return C === undefined || (S = anObject(C)[SPECIES]) == undefined ? D : aFunction(S);\n};\n","var isObject = require('./_is-object');\nvar isArray = require('./_is-array');\nvar SPECIES = require('./_wks')('species');\n\nmodule.exports = function (original) {\n var C;\n if (isArray(original)) {\n C = original.constructor;\n // cross-realm fallback\n if (typeof C == 'function' && (C === Array || isArray(C.prototype))) C = undefined;\n if (isObject(C)) {\n C = C[SPECIES];\n if (C === null) C = undefined;\n }\n } return C === undefined ? Array : C;\n};\n","'use strict';\n\n/**\n * Check if `obj` is an object.\n *\n * @param {Object} obj\n * @return {Boolean}\n * @api private\n */\n\nfunction isObject(obj) {\n return null !== obj && 'object' === typeof obj;\n}\n\nmodule.exports = isObject;\n","// 19.1.3.1 Object.assign(target, source)\nvar $export = require('./_export');\n\n$export($export.S + $export.F, 'Object', { assign: require('./_object-assign') });\n","module.exports = function (it, Constructor, name, forbiddenField) {\n if (!(it instanceof Constructor) || (forbiddenField !== undefined && forbiddenField in it)) {\n throw TypeError(name + ': incorrect invocation!');\n } return it;\n};\n","'use strict';\nvar LIBRARY = require('./_library');\nvar $export = require('./_export');\nvar redefine = require('./_redefine');\nvar hide = require('./_hide');\nvar Iterators = require('./_iterators');\nvar $iterCreate = require('./_iter-create');\nvar setToStringTag = require('./_set-to-string-tag');\nvar getPrototypeOf = require('./_object-gpo');\nvar ITERATOR = require('./_wks')('iterator');\nvar BUGGY = !([].keys && 'next' in [].keys()); // Safari has buggy iterators w/o `next`\nvar FF_ITERATOR = '@@iterator';\nvar KEYS = 'keys';\nvar VALUES = 'values';\n\nvar returnThis = function () { return this; };\n\nmodule.exports = function (Base, NAME, Constructor, next, DEFAULT, IS_SET, FORCED) {\n $iterCreate(Constructor, NAME, next);\n var getMethod = function (kind) {\n if (!BUGGY && kind in proto) return proto[kind];\n switch (kind) {\n case KEYS: return function keys() { return new Constructor(this, kind); };\n case VALUES: return function values() { return new Constructor(this, kind); };\n } return function entries() { return new Constructor(this, kind); };\n };\n var TAG = NAME + ' Iterator';\n var DEF_VALUES = DEFAULT == VALUES;\n var VALUES_BUG = false;\n var proto = Base.prototype;\n var $native = proto[ITERATOR] || proto[FF_ITERATOR] || DEFAULT && proto[DEFAULT];\n var $default = $native || getMethod(DEFAULT);\n var $entries = DEFAULT ? !DEF_VALUES ? $default : getMethod('entries') : undefined;\n var $anyNative = NAME == 'Array' ? proto.entries || $native : $native;\n var methods, key, IteratorPrototype;\n // Fix native\n if ($anyNative) {\n IteratorPrototype = getPrototypeOf($anyNative.call(new Base()));\n if (IteratorPrototype !== Object.prototype && IteratorPrototype.next) {\n // Set @@toStringTag to native iterators\n setToStringTag(IteratorPrototype, TAG, true);\n // fix for some old engines\n if (!LIBRARY && typeof IteratorPrototype[ITERATOR] != 'function') hide(IteratorPrototype, ITERATOR, returnThis);\n }\n }\n // fix Array#{values, @@iterator}.name in V8 / FF\n if (DEF_VALUES && $native && $native.name !== VALUES) {\n VALUES_BUG = true;\n $default = function values() { return $native.call(this); };\n }\n // Define iterator\n if ((!LIBRARY || FORCED) && (BUGGY || VALUES_BUG || !proto[ITERATOR])) {\n hide(proto, ITERATOR, $default);\n }\n // Plug for library\n Iterators[NAME] = $default;\n Iterators[TAG] = returnThis;\n if (DEFAULT) {\n methods = {\n values: DEF_VALUES ? $default : getMethod(VALUES),\n keys: IS_SET ? $default : getMethod(KEYS),\n entries: $entries\n };\n if (FORCED) for (key in methods) {\n if (!(key in proto)) redefine(proto, key, methods[key]);\n } else $export($export.P + $export.F * (BUGGY || VALUES_BUG), NAME, methods);\n }\n return methods;\n};\n","'use strict';\n// 21.2.5.3 get RegExp.prototype.flags\nvar anObject = require('./_an-object');\nmodule.exports = function () {\n var that = anObject(this);\n var result = '';\n if (that.global) result += 'g';\n if (that.ignoreCase) result += 'i';\n if (that.multiline) result += 'm';\n if (that.unicode) result += 'u';\n if (that.sticky) result += 'y';\n return result;\n};\n","// 0 -> Array#forEach\n// 1 -> Array#map\n// 2 -> Array#filter\n// 3 -> Array#some\n// 4 -> Array#every\n// 5 -> Array#find\n// 6 -> Array#findIndex\nvar ctx = require('./_ctx');\nvar IObject = require('./_iobject');\nvar toObject = require('./_to-object');\nvar toLength = require('./_to-length');\nvar asc = require('./_array-species-create');\nmodule.exports = function (TYPE, $create) {\n var IS_MAP = TYPE == 1;\n var IS_FILTER = TYPE == 2;\n var IS_SOME = TYPE == 3;\n var IS_EVERY = TYPE == 4;\n var IS_FIND_INDEX = TYPE == 6;\n var NO_HOLES = TYPE == 5 || IS_FIND_INDEX;\n var create = $create || asc;\n return function ($this, callbackfn, that) {\n var O = toObject($this);\n var self = IObject(O);\n var f = ctx(callbackfn, that, 3);\n var length = toLength(self.length);\n var index = 0;\n var result = IS_MAP ? create($this, length) : IS_FILTER ? create($this, 0) : undefined;\n var val, res;\n for (;length > index; index++) if (NO_HOLES || index in self) {\n val = self[index];\n res = f(val, index, O);\n if (TYPE) {\n if (IS_MAP) result[index] = res; // map\n else if (res) switch (TYPE) {\n case 3: return true; // some\n case 5: return val; // find\n case 6: return index; // findIndex\n case 2: result.push(val); // filter\n } else if (IS_EVERY) return false; // every\n }\n }\n return IS_FIND_INDEX ? -1 : IS_SOME || IS_EVERY ? IS_EVERY : result;\n };\n};\n","// 19.1.2.14 / 15.2.3.14 Object.keys(O)\nvar $keys = require('./_object-keys-internal');\nvar enumBugKeys = require('./_enum-bug-keys');\n\nmodule.exports = Object.keys || function keys(O) {\n return $keys(O, enumBugKeys);\n};\n","/**\n * marked - a markdown parser\n * Copyright (c) 2011-2014, Christopher Jeffrey. (MIT Licensed)\n * https://github.com/markedjs/marked\n */\n\n;(function(root) {\n'use strict';\n\n/**\n * Block-Level Grammar\n */\n\nvar block = {\n newline: /^\\n+/,\n code: /^( {4}[^\\n]+\\n*)+/,\n fences: noop,\n hr: /^ {0,3}((?:- *){3,}|(?:_ *){3,}|(?:\\* *){3,})(?:\\n+|$)/,\n heading: /^ *(#{1,6}) *([^\\n]+?) *(?:#+ *)?(?:\\n+|$)/,\n nptable: noop,\n blockquote: /^( {0,3}> ?(paragraph|[^\\n]*)(?:\\n|$))+/,\n list: /^( *)(bull) [\\s\\S]+?(?:hr|def|\\n{2,}(?! )(?!\\1bull )\\n*|\\s*$)/,\n html: '^ {0,3}(?:' // optional indentation\n + '<(script|pre|style)[\\\\s>][\\\\s\\\\S]*?(?:[^\\\\n]*\\\\n+|$)' // (1)\n + '|comment[^\\\\n]*(\\\\n+|$)' // (2)\n + '|<\\\\?[\\\\s\\\\S]*?\\\\?>\\\\n*' // (3)\n + '|\\\\n*' // (4)\n + '|\\\\n*' // (5)\n + '|)[\\\\s\\\\S]*?(?:\\\\n{2,}|$)' // (6)\n + '|<(?!script|pre|style)([a-z][\\\\w-]*)(?:attribute)*? */?>(?=\\\\h*\\\\n)[\\\\s\\\\S]*?(?:\\\\n{2,}|$)' // (7) open tag\n + '|(?=\\\\h*\\\\n)[\\\\s\\\\S]*?(?:\\\\n{2,}|$)' // (7) closing tag\n + ')',\n def: /^ {0,3}\\[(label)\\]: *\\n? *]+)>?(?:(?: +\\n? *| *\\n *)(title))? *(?:\\n+|$)/,\n table: noop,\n lheading: /^([^\\n]+)\\n *(=|-){2,} *(?:\\n+|$)/,\n paragraph: /^([^\\n]+(?:\\n(?!hr|heading|lheading| {0,3}>|<\\/?(?:tag)(?: +|\\n|\\/?>)|<(?:script|pre|style|!--))[^\\n]+)*)/,\n text: /^[^\\n]+/\n};\n\nblock._label = /(?!\\s*\\])(?:\\\\[\\[\\]]|[^\\[\\]])+/;\nblock._title = /(?:\"(?:\\\\\"?|[^\"\\\\])*\"|'[^'\\n]*(?:\\n[^'\\n]+)*\\n?'|\\([^()]*\\))/;\nblock.def = edit(block.def)\n .replace('label', block._label)\n .replace('title', block._title)\n .getRegex();\n\nblock.bullet = /(?:[*+-]|\\d+\\.)/;\nblock.item = /^( *)(bull) [^\\n]*(?:\\n(?!\\1bull )[^\\n]*)*/;\nblock.item = edit(block.item, 'gm')\n .replace(/bull/g, block.bullet)\n .getRegex();\n\nblock.list = edit(block.list)\n .replace(/bull/g, block.bullet)\n .replace('hr', '\\\\n+(?=\\\\1?(?:(?:- *){3,}|(?:_ *){3,}|(?:\\\\* *){3,})(?:\\\\n+|$))')\n .replace('def', '\\\\n+(?=' + block.def.source + ')')\n .getRegex();\n\nblock._tag = 'address|article|aside|base|basefont|blockquote|body|caption'\n + '|center|col|colgroup|dd|details|dialog|dir|div|dl|dt|fieldset|figcaption'\n + '|figure|footer|form|frame|frameset|h[1-6]|head|header|hr|html|iframe'\n + '|legend|li|link|main|menu|menuitem|meta|nav|noframes|ol|optgroup|option'\n + '|p|param|section|source|summary|table|tbody|td|tfoot|th|thead|title|tr'\n + '|track|ul';\nblock._comment = //;\nblock.html = edit(block.html, 'i')\n .replace('comment', block._comment)\n .replace('tag', block._tag)\n .replace('attribute', / +[a-zA-Z:_][\\w.:-]*(?: *= *\"[^\"\\n]*\"| *= *'[^'\\n]*'| *= *[^\\s\"'=<>`]+)?/)\n .getRegex();\n\nblock.paragraph = edit(block.paragraph)\n .replace('hr', block.hr)\n .replace('heading', block.heading)\n .replace('lheading', block.lheading)\n .replace('tag', block._tag) // pars can be interrupted by type (6) html blocks\n .getRegex();\n\nblock.blockquote = edit(block.blockquote)\n .replace('paragraph', block.paragraph)\n .getRegex();\n\n/**\n * Normal Block Grammar\n */\n\nblock.normal = merge({}, block);\n\n/**\n * GFM Block Grammar\n */\n\nblock.gfm = merge({}, block.normal, {\n fences: /^ *(`{3,}|~{3,})[ \\.]*(\\S+)? *\\n([\\s\\S]*?)\\n? *\\1 *(?:\\n+|$)/,\n paragraph: /^/,\n heading: /^ *(#{1,6}) +([^\\n]+?) *#* *(?:\\n+|$)/\n});\n\nblock.gfm.paragraph = edit(block.paragraph)\n .replace('(?!', '(?!'\n + block.gfm.fences.source.replace('\\\\1', '\\\\2') + '|'\n + block.list.source.replace('\\\\1', '\\\\3') + '|')\n .getRegex();\n\n/**\n * GFM + Tables Block Grammar\n */\n\nblock.tables = merge({}, block.gfm, {\n nptable: /^ *([^|\\n ].*\\|.*)\\n *([-:]+ *\\|[-| :]*)(?:\\n((?:.*[^>\\n ].*(?:\\n|$))*)\\n*|$)/,\n table: /^ *\\|(.+)\\n *\\|?( *[-:]+[-| :]*)(?:\\n((?: *[^>\\n ].*(?:\\n|$))*)\\n*|$)/\n});\n\n/**\n * Pedantic grammar\n */\n\nblock.pedantic = merge({}, block.normal, {\n html: edit(\n '^ *(?:comment *(?:\\\\n|\\\\s*$)'\n + '|<(tag)[\\\\s\\\\S]+? *(?:\\\\n{2,}|\\\\s*$)' // closed tag\n + '|\\\\s]*)*?/?> *(?:\\\\n{2,}|\\\\s*$))')\n .replace('comment', block._comment)\n .replace(/tag/g, '(?!(?:'\n + 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code|var|samp|kbd|sub'\n + '|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo|span|br|wbr|ins|del|img)'\n + '\\\\b)\\\\w+(?!:|[^\\\\w\\\\s@]*@)\\\\b')\n .getRegex(),\n def: /^ *\\[([^\\]]+)\\]: *]+)>?(?: +([\"(][^\\n]+[\")]))? *(?:\\n+|$)/\n});\n\n/**\n * Block Lexer\n */\n\nfunction Lexer(options) {\n this.tokens = [];\n this.tokens.links = {};\n this.options = options || marked.defaults;\n this.rules = block.normal;\n\n if (this.options.pedantic) {\n this.rules = block.pedantic;\n } else if (this.options.gfm) {\n if (this.options.tables) {\n this.rules = block.tables;\n } else {\n this.rules = block.gfm;\n }\n }\n}\n\n/**\n * Expose Block Rules\n */\n\nLexer.rules = block;\n\n/**\n * Static Lex Method\n */\n\nLexer.lex = function(src, options) {\n var lexer = new Lexer(options);\n return lexer.lex(src);\n};\n\n/**\n * Preprocessing\n */\n\nLexer.prototype.lex = function(src) {\n src = src\n .replace(/\\r\\n|\\r/g, '\\n')\n .replace(/\\t/g, ' ')\n .replace(/\\u00a0/g, ' ')\n .replace(/\\u2424/g, '\\n');\n\n return this.token(src, true);\n};\n\n/**\n * Lexing\n */\n\nLexer.prototype.token = function(src, top) {\n src = src.replace(/^ +$/gm, '');\n var next,\n loose,\n cap,\n bull,\n b,\n item,\n space,\n i,\n tag,\n l,\n isordered,\n istask,\n ischecked;\n\n while (src) {\n // newline\n if (cap = this.rules.newline.exec(src)) {\n src = src.substring(cap[0].length);\n if (cap[0].length > 1) {\n this.tokens.push({\n type: 'space'\n });\n }\n }\n\n // code\n if (cap = this.rules.code.exec(src)) {\n src = src.substring(cap[0].length);\n cap = cap[0].replace(/^ {4}/gm, '');\n this.tokens.push({\n type: 'code',\n text: !this.options.pedantic\n ? cap.replace(/\\n+$/, '')\n : cap\n });\n continue;\n }\n\n // fences (gfm)\n if (cap = this.rules.fences.exec(src)) {\n src = src.substring(cap[0].length);\n this.tokens.push({\n type: 'code',\n lang: cap[2],\n text: cap[3] || ''\n });\n continue;\n }\n\n // heading\n if (cap = this.rules.heading.exec(src)) {\n src = src.substring(cap[0].length);\n this.tokens.push({\n type: 'heading',\n depth: cap[1].length,\n text: cap[2]\n });\n continue;\n }\n\n // table no leading pipe (gfm)\n if (top && (cap = this.rules.nptable.exec(src))) {\n item = {\n type: 'table',\n header: splitCells(cap[1].replace(/^ *| *\\| *$/g, '')),\n align: cap[2].replace(/^ *|\\| *$/g, '').split(/ *\\| */),\n cells: cap[3] ? cap[3].replace(/\\n$/, '').split('\\n') : []\n };\n\n if (item.header.length === item.align.length) {\n src = src.substring(cap[0].length);\n\n for (i = 0; i < item.align.length; i++) {\n if (/^ *-+: *$/.test(item.align[i])) {\n item.align[i] = 'right';\n } else if (/^ *:-+: *$/.test(item.align[i])) {\n item.align[i] = 'center';\n } else if (/^ *:-+ *$/.test(item.align[i])) {\n item.align[i] = 'left';\n } else {\n item.align[i] = null;\n }\n }\n\n for (i = 0; i < item.cells.length; i++) {\n item.cells[i] = splitCells(item.cells[i], item.header.length);\n }\n\n this.tokens.push(item);\n\n continue;\n }\n }\n\n // hr\n if (cap = this.rules.hr.exec(src)) {\n src = src.substring(cap[0].length);\n this.tokens.push({\n type: 'hr'\n });\n continue;\n }\n\n // blockquote\n if (cap = this.rules.blockquote.exec(src)) {\n src = src.substring(cap[0].length);\n\n this.tokens.push({\n type: 'blockquote_start'\n });\n\n cap = cap[0].replace(/^ *> ?/gm, '');\n\n // Pass `top` to keep the current\n // \"toplevel\" state. This is exactly\n // how markdown.pl works.\n this.token(cap, top);\n\n this.tokens.push({\n type: 'blockquote_end'\n });\n\n continue;\n }\n\n // list\n if (cap = this.rules.list.exec(src)) {\n src = src.substring(cap[0].length);\n bull = cap[2];\n isordered = bull.length > 1;\n\n this.tokens.push({\n type: 'list_start',\n ordered: isordered,\n start: isordered ? +bull : ''\n });\n\n // Get each top-level item.\n cap = cap[0].match(this.rules.item);\n\n next = false;\n l = cap.length;\n i = 0;\n\n for (; i < l; i++) {\n item = cap[i];\n\n // Remove the list item's bullet\n // so it is seen as the next token.\n space = item.length;\n item = item.replace(/^ *([*+-]|\\d+\\.) +/, '');\n\n // Outdent whatever the\n // list item contains. Hacky.\n if (~item.indexOf('\\n ')) {\n space -= item.length;\n item = !this.options.pedantic\n ? item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '')\n : item.replace(/^ {1,4}/gm, '');\n }\n\n // Determine whether the next list item belongs here.\n // Backpedal if it does not belong in this list.\n if (this.options.smartLists && i !== l - 1) {\n b = block.bullet.exec(cap[i + 1])[0];\n if (bull !== b && !(bull.length > 1 && b.length > 1)) {\n src = cap.slice(i + 1).join('\\n') + src;\n i = l - 1;\n }\n }\n\n // Determine whether item is loose or not.\n // Use: /(^|\\n)(?! )[^\\n]+\\n\\n(?!\\s*$)/\n // for discount behavior.\n loose = next || /\\n\\n(?!\\s*$)/.test(item);\n if (i !== l - 1) {\n next = item.charAt(item.length - 1) === '\\n';\n if (!loose) loose = next;\n }\n\n // Check for task list items\n istask = /^\\[[ xX]\\] /.test(item);\n ischecked = undefined;\n if (istask) {\n ischecked = item[1] !== ' ';\n item = item.replace(/^\\[[ xX]\\] +/, '');\n }\n\n this.tokens.push({\n type: loose\n ? 'loose_item_start'\n : 'list_item_start',\n task: istask,\n checked: ischecked\n });\n\n // Recurse.\n this.token(item, false);\n\n this.tokens.push({\n type: 'list_item_end'\n });\n }\n\n this.tokens.push({\n type: 'list_end'\n });\n\n continue;\n }\n\n // html\n if (cap = this.rules.html.exec(src)) {\n src = src.substring(cap[0].length);\n this.tokens.push({\n type: this.options.sanitize\n ? 'paragraph'\n : 'html',\n pre: !this.options.sanitizer\n && (cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style'),\n text: cap[0]\n });\n continue;\n }\n\n // def\n if (top && (cap = this.rules.def.exec(src))) {\n src = src.substring(cap[0].length);\n if (cap[3]) cap[3] = cap[3].substring(1, cap[3].length - 1);\n tag = cap[1].toLowerCase().replace(/\\s+/g, ' ');\n if (!this.tokens.links[tag]) {\n this.tokens.links[tag] = {\n href: cap[2],\n title: cap[3]\n };\n }\n continue;\n }\n\n // table (gfm)\n if (top && (cap = this.rules.table.exec(src))) {\n item = {\n type: 'table',\n header: splitCells(cap[1].replace(/^ *| *\\| *$/g, '')),\n align: cap[2].replace(/^ *|\\| *$/g, '').split(/ *\\| */),\n cells: cap[3] ? cap[3].replace(/(?: *\\| *)?\\n$/, '').split('\\n') : []\n };\n\n if (item.header.length === item.align.length) {\n src = src.substring(cap[0].length);\n\n for (i = 0; i < item.align.length; i++) {\n if (/^ *-+: *$/.test(item.align[i])) {\n item.align[i] = 'right';\n } else if (/^ *:-+: *$/.test(item.align[i])) {\n item.align[i] = 'center';\n } else if (/^ *:-+ *$/.test(item.align[i])) {\n item.align[i] = 'left';\n } else {\n item.align[i] = null;\n }\n }\n\n for (i = 0; i < item.cells.length; i++) {\n item.cells[i] = splitCells(\n item.cells[i].replace(/^ *\\| *| *\\| *$/g, ''),\n item.header.length);\n }\n\n this.tokens.push(item);\n\n continue;\n }\n }\n\n // lheading\n if (cap = this.rules.lheading.exec(src)) {\n src = src.substring(cap[0].length);\n this.tokens.push({\n type: 'heading',\n depth: cap[2] === '=' ? 1 : 2,\n text: cap[1]\n });\n continue;\n }\n\n // top-level paragraph\n if (top && (cap = this.rules.paragraph.exec(src))) {\n src = src.substring(cap[0].length);\n this.tokens.push({\n type: 'paragraph',\n text: cap[1].charAt(cap[1].length - 1) === '\\n'\n ? cap[1].slice(0, -1)\n : cap[1]\n });\n continue;\n }\n\n // text\n if (cap = this.rules.text.exec(src)) {\n // Top-level should never reach here.\n src = src.substring(cap[0].length);\n this.tokens.push({\n type: 'text',\n text: cap[0]\n });\n continue;\n }\n\n if (src) {\n throw new Error('Infinite loop on byte: ' + src.charCodeAt(0));\n }\n }\n\n return this.tokens;\n};\n\n/**\n * Inline-Level Grammar\n */\n\nvar inline = {\n escape: /^\\\\([!\"#$%&'()*+,\\-./:;<=>?@\\[\\]\\\\^_`{|}~])/,\n autolink: /^<(scheme:[^\\s\\x00-\\x1f<>]*|email)>/,\n url: noop,\n tag: '^comment'\n + '|^' // self-closing tag\n + '|^<[a-zA-Z][\\\\w-]*(?:attribute)*?\\\\s*/?>' // open tag\n + '|^<\\\\?[\\\\s\\\\S]*?\\\\?>' // processing instruction, e.g. \n + '|^' // declaration, e.g. \n + '|^', // CDATA section\n link: /^!?\\[(label)\\]\\(href(?:\\s+(title))?\\s*\\)/,\n reflink: /^!?\\[(label)\\]\\[(?!\\s*\\])((?:\\\\[\\[\\]]?|[^\\[\\]\\\\])+)\\]/,\n nolink: /^!?\\[(?!\\s*\\])((?:\\[[^\\[\\]]*\\]|\\\\[\\[\\]]|[^\\[\\]])*)\\](?:\\[\\])?/,\n strong: /^__([^\\s][\\s\\S]*?[^\\s])__(?!_)|^\\*\\*([^\\s][\\s\\S]*?[^\\s])\\*\\*(?!\\*)|^__([^\\s])__(?!_)|^\\*\\*([^\\s])\\*\\*(?!\\*)/,\n em: /^_([^\\s][\\s\\S]*?[^\\s_])_(?!_)|^_([^\\s_][\\s\\S]*?[^\\s])_(?!_)|^\\*([^\\s][\\s\\S]*?[^\\s*])\\*(?!\\*)|^\\*([^\\s*][\\s\\S]*?[^\\s])\\*(?!\\*)|^_([^\\s_])_(?!_)|^\\*([^\\s*])\\*(?!\\*)/,\n code: /^(`+)\\s*([\\s\\S]*?[^`]?)\\s*\\1(?!`)/,\n br: /^ {2,}\\n(?!\\s*$)/,\n del: noop,\n text: /^[\\s\\S]+?(?=[\\\\?@\\[\\]\\\\^_`{|}~])/g;\n\ninline._scheme = /[a-zA-Z][a-zA-Z0-9+.-]{1,31}/;\ninline._email = /[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+(@)[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+(?![-_])/;\ninline.autolink = edit(inline.autolink)\n .replace('scheme', inline._scheme)\n .replace('email', inline._email)\n .getRegex();\n\ninline._attribute = /\\s+[a-zA-Z:_][\\w.:-]*(?:\\s*=\\s*\"[^\"]*\"|\\s*=\\s*'[^']*'|\\s*=\\s*[^\\s\"'=<>`]+)?/;\n\ninline.tag = edit(inline.tag)\n .replace('comment', block._comment)\n .replace('attribute', inline._attribute)\n .getRegex();\n\ninline._label = /(?:\\[[^\\[\\]]*\\]|\\\\[\\[\\]]?|`[^`]*`|[^\\[\\]\\\\])*?/;\ninline._href = /\\s*(<(?:\\\\[<>]?|[^\\s<>\\\\])*>|(?:\\\\[()]?|\\([^\\s\\x00-\\x1f()\\\\]*\\)|[^\\s\\x00-\\x1f()\\\\])*?)/;\ninline._title = /\"(?:\\\\\"?|[^\"\\\\])*\"|'(?:\\\\'?|[^'\\\\])*'|\\((?:\\\\\\)?|[^)\\\\])*\\)/;\n\ninline.link = edit(inline.link)\n .replace('label', inline._label)\n .replace('href', inline._href)\n .replace('title', inline._title)\n .getRegex();\n\ninline.reflink = edit(inline.reflink)\n .replace('label', inline._label)\n .getRegex();\n\n/**\n * Normal Inline Grammar\n */\n\ninline.normal = merge({}, inline);\n\n/**\n * Pedantic Inline Grammar\n */\n\ninline.pedantic = merge({}, inline.normal, {\n strong: /^__(?=\\S)([\\s\\S]*?\\S)__(?!_)|^\\*\\*(?=\\S)([\\s\\S]*?\\S)\\*\\*(?!\\*)/,\n em: /^_(?=\\S)([\\s\\S]*?\\S)_(?!_)|^\\*(?=\\S)([\\s\\S]*?\\S)\\*(?!\\*)/,\n link: edit(/^!?\\[(label)\\]\\((.*?)\\)/)\n .replace('label', inline._label)\n .getRegex(),\n reflink: edit(/^!?\\[(label)\\]\\s*\\[([^\\]]*)\\]/)\n .replace('label', inline._label)\n .getRegex()\n});\n\n/**\n * GFM Inline Grammar\n */\n\ninline.gfm = merge({}, inline.normal, {\n escape: edit(inline.escape).replace('])', '~|])').getRegex(),\n url: edit(/^((?:ftp|https?):\\/\\/|www\\.)(?:[a-zA-Z0-9\\-]+\\.?)+[^\\s<]*|^email/)\n .replace('email', inline._email)\n .getRegex(),\n _backpedal: /(?:[^?!.,:;*_~()&]+|\\([^)]*\\)|&(?![a-zA-Z0-9]+;$)|[?!.,:;*_~)]+(?!$))+/,\n del: /^~~(?=\\S)([\\s\\S]*?\\S)~~/,\n text: edit(inline.text)\n .replace(']|', '~]|')\n .replace('|', '|https?://|ftp://|www\\\\.|[a-zA-Z0-9.!#$%&\\'*+/=?^_`{\\\\|}~-]+@|')\n .getRegex()\n});\n\n/**\n * GFM + Line Breaks Inline Grammar\n */\n\ninline.breaks = merge({}, inline.gfm, {\n br: edit(inline.br).replace('{2,}', '*').getRegex(),\n text: edit(inline.gfm.text).replace('{2,}', '*').getRegex()\n});\n\n/**\n * Inline Lexer & Compiler\n */\n\nfunction InlineLexer(links, options) {\n this.options = options || marked.defaults;\n this.links = links;\n this.rules = inline.normal;\n this.renderer = this.options.renderer || new Renderer();\n this.renderer.options = this.options;\n\n if (!this.links) {\n throw new Error('Tokens array requires a `links` property.');\n }\n\n if (this.options.pedantic) {\n this.rules = inline.pedantic;\n } else if (this.options.gfm) {\n if (this.options.breaks) {\n this.rules = inline.breaks;\n } else {\n this.rules = inline.gfm;\n }\n }\n}\n\n/**\n * Expose Inline Rules\n */\n\nInlineLexer.rules = inline;\n\n/**\n * Static Lexing/Compiling Method\n */\n\nInlineLexer.output = function(src, links, options) {\n var inline = new InlineLexer(links, options);\n return inline.output(src);\n};\n\n/**\n * Lexing/Compiling\n */\n\nInlineLexer.prototype.output = function(src) {\n var out = '',\n link,\n text,\n href,\n title,\n cap;\n\n while (src) {\n // escape\n if (cap = this.rules.escape.exec(src)) {\n src = src.substring(cap[0].length);\n out += cap[1];\n continue;\n }\n\n // autolink\n if (cap = this.rules.autolink.exec(src)) {\n src = src.substring(cap[0].length);\n if (cap[2] === '@') {\n text = escape(this.mangle(cap[1]));\n href = 'mailto:' + text;\n } else {\n text = escape(cap[1]);\n href = text;\n }\n out += this.renderer.link(href, null, text);\n continue;\n }\n\n // url (gfm)\n if (!this.inLink && (cap = this.rules.url.exec(src))) {\n cap[0] = this.rules._backpedal.exec(cap[0])[0];\n src = src.substring(cap[0].length);\n if (cap[2] === '@') {\n text = escape(cap[0]);\n href = 'mailto:' + text;\n } else {\n text = escape(cap[0]);\n if (cap[1] === 'www.') {\n href = 'http://' + text;\n } else {\n href = text;\n }\n }\n out += this.renderer.link(href, null, text);\n continue;\n }\n\n // tag\n if (cap = this.rules.tag.exec(src)) {\n if (!this.inLink && /^/i.test(cap[0])) {\n this.inLink = false;\n }\n src = src.substring(cap[0].length);\n out += this.options.sanitize\n ? this.options.sanitizer\n ? this.options.sanitizer(cap[0])\n : escape(cap[0])\n : cap[0]\n continue;\n }\n\n // link\n if (cap = this.rules.link.exec(src)) {\n src = src.substring(cap[0].length);\n this.inLink = true;\n href = cap[2];\n if (this.options.pedantic) {\n link = /^([^'\"]*[^\\s])\\s+(['\"])(.*)\\2/.exec(href);\n\n if (link) {\n href = link[1];\n title = link[3];\n } else {\n title = '';\n }\n } else {\n title = cap[3] ? cap[3].slice(1, -1) : '';\n }\n href = href.trim().replace(/^<([\\s\\S]*)>$/, '$1');\n out += this.outputLink(cap, {\n href: InlineLexer.escapes(href),\n title: InlineLexer.escapes(title)\n });\n this.inLink = false;\n continue;\n }\n\n // reflink, nolink\n if ((cap = this.rules.reflink.exec(src))\n || (cap = this.rules.nolink.exec(src))) {\n src = src.substring(cap[0].length);\n link = (cap[2] || cap[1]).replace(/\\s+/g, ' ');\n link = this.links[link.toLowerCase()];\n if (!link || !link.href) {\n out += cap[0].charAt(0);\n src = cap[0].substring(1) + src;\n continue;\n }\n this.inLink = true;\n out += this.outputLink(cap, link);\n this.inLink = false;\n continue;\n }\n\n // strong\n if (cap = this.rules.strong.exec(src)) {\n src = src.substring(cap[0].length);\n out += this.renderer.strong(this.output(cap[4] || cap[3] || cap[2] || cap[1]));\n continue;\n }\n\n // em\n if (cap = this.rules.em.exec(src)) {\n src = src.substring(cap[0].length);\n out += this.renderer.em(this.output(cap[6] || cap[5] || cap[4] || cap[3] || cap[2] || cap[1]));\n continue;\n }\n\n // code\n if (cap = this.rules.code.exec(src)) {\n src = src.substring(cap[0].length);\n out += this.renderer.codespan(escape(cap[2].trim(), true));\n continue;\n }\n\n // br\n if (cap = this.rules.br.exec(src)) {\n src = src.substring(cap[0].length);\n out += this.renderer.br();\n continue;\n }\n\n // del (gfm)\n if (cap = this.rules.del.exec(src)) {\n src = src.substring(cap[0].length);\n out += this.renderer.del(this.output(cap[1]));\n continue;\n }\n\n // text\n if (cap = this.rules.text.exec(src)) {\n src = src.substring(cap[0].length);\n out += this.renderer.text(escape(this.smartypants(cap[0])));\n continue;\n }\n\n if (src) {\n throw new Error('Infinite loop on byte: ' + src.charCodeAt(0));\n }\n }\n\n return out;\n};\n\nInlineLexer.escapes = function(text) {\n return text ? text.replace(InlineLexer.rules._escapes, '$1') : text;\n}\n\n/**\n * Compile Link\n */\n\nInlineLexer.prototype.outputLink = function(cap, link) {\n var href = link.href,\n title = link.title ? escape(link.title) : null;\n\n return cap[0].charAt(0) !== '!'\n ? this.renderer.link(href, title, this.output(cap[1]))\n : this.renderer.image(href, title, escape(cap[1]));\n};\n\n/**\n * Smartypants Transformations\n */\n\nInlineLexer.prototype.smartypants = function(text) {\n if (!this.options.smartypants) return text;\n return text\n // em-dashes\n .replace(/---/g, '\\u2014')\n // en-dashes\n .replace(/--/g, '\\u2013')\n // opening singles\n .replace(/(^|[-\\u2014/(\\[{\"\\s])'/g, '$1\\u2018')\n // closing singles & apostrophes\n .replace(/'/g, '\\u2019')\n // opening doubles\n .replace(/(^|[-\\u2014/(\\[{\\u2018\\s])\"/g, '$1\\u201c')\n // closing doubles\n .replace(/\"/g, '\\u201d')\n // ellipses\n .replace(/\\.{3}/g, '\\u2026');\n};\n\n/**\n * Mangle Links\n */\n\nInlineLexer.prototype.mangle = function(text) {\n if (!this.options.mangle) return text;\n var out = '',\n l = text.length,\n i = 0,\n ch;\n\n for (; i < l; i++) {\n ch = text.charCodeAt(i);\n if (Math.random() > 0.5) {\n ch = 'x' + ch.toString(16);\n }\n out += '&#' + ch + ';';\n }\n\n return out;\n};\n\n/**\n * Renderer\n */\n\nfunction Renderer(options) {\n this.options = options || marked.defaults;\n}\n\nRenderer.prototype.code = function(code, lang, escaped) {\n if (this.options.highlight) {\n var out = this.options.highlight(code, lang);\n if (out != null && out !== code) {\n escaped = true;\n code = out;\n }\n }\n\n if (!lang) {\n return '
    '\n      + (escaped ? code : escape(code, true))\n      + '
    ';\n }\n\n return '
    '\n    + (escaped ? code : escape(code, true))\n    + '
    \\n';\n};\n\nRenderer.prototype.blockquote = function(quote) {\n return '
    \\n' + quote + '
    \\n';\n};\n\nRenderer.prototype.html = function(html) {\n return html;\n};\n\nRenderer.prototype.heading = function(text, level, raw) {\n if (this.options.headerIds) {\n return ''\n + text\n + '\\n';\n }\n // ignore IDs\n return '' + text + '\\n';\n};\n\nRenderer.prototype.hr = function() {\n return this.options.xhtml ? '
    \\n' : '
    \\n';\n};\n\nRenderer.prototype.list = function(body, ordered, start) {\n var type = ordered ? 'ol' : 'ul',\n startatt = (ordered && start !== 1) ? (' start=\"' + start + '\"') : '';\n return '<' + type + startatt + '>\\n' + body + '\\n';\n};\n\nRenderer.prototype.listitem = function(text) {\n return '
  • ' + text + '
  • \\n';\n};\n\nRenderer.prototype.checkbox = function(checked) {\n return ' ';\n}\n\nRenderer.prototype.paragraph = function(text) {\n return '

    ' + text + '

    \\n';\n};\n\nRenderer.prototype.table = function(header, body) {\n if (body) body = '' + body + '';\n\n return '\\n'\n + '\\n'\n + header\n + '\\n'\n + body\n + '
    \\n';\n};\n\nRenderer.prototype.tablerow = function(content) {\n return '\\n' + content + '\\n';\n};\n\nRenderer.prototype.tablecell = function(content, flags) {\n var type = flags.header ? 'th' : 'td';\n var tag = flags.align\n ? '<' + type + ' align=\"' + flags.align + '\">'\n : '<' + type + '>';\n return tag + content + '\\n';\n};\n\n// span level renderer\nRenderer.prototype.strong = function(text) {\n return '' + text + '';\n};\n\nRenderer.prototype.em = function(text) {\n return '' + text + '';\n};\n\nRenderer.prototype.codespan = function(text) {\n return '' + text + '';\n};\n\nRenderer.prototype.br = function() {\n return this.options.xhtml ? '
    ' : '
    ';\n};\n\nRenderer.prototype.del = function(text) {\n return '' + text + '';\n};\n\nRenderer.prototype.link = function(href, title, text) {\n if (this.options.sanitize) {\n try {\n var prot = decodeURIComponent(unescape(href))\n .replace(/[^\\w:]/g, '')\n .toLowerCase();\n } catch (e) {\n return text;\n }\n if (prot.indexOf('javascript:') === 0 || prot.indexOf('vbscript:') === 0 || prot.indexOf('data:') === 0) {\n return text;\n }\n }\n if (this.options.baseUrl && !originIndependentUrl.test(href)) {\n href = resolveUrl(this.options.baseUrl, href);\n }\n try {\n href = encodeURI(href).replace(/%25/g, '%');\n } catch (e) {\n return text;\n }\n var out = '
    ';\n return out;\n};\n\nRenderer.prototype.image = function(href, title, text) {\n if (this.options.baseUrl && !originIndependentUrl.test(href)) {\n href = resolveUrl(this.options.baseUrl, href);\n }\n var out = '\"'' : '>';\n return out;\n};\n\nRenderer.prototype.text = function(text) {\n return text;\n};\n\n/**\n * TextRenderer\n * returns only the textual part of the token\n */\n\nfunction TextRenderer() {}\n\n// no need for block level renderers\n\nTextRenderer.prototype.strong =\nTextRenderer.prototype.em =\nTextRenderer.prototype.codespan =\nTextRenderer.prototype.del =\nTextRenderer.prototype.text = function (text) {\n return text;\n}\n\nTextRenderer.prototype.link =\nTextRenderer.prototype.image = function(href, title, text) {\n return '' + text;\n}\n\nTextRenderer.prototype.br = function() {\n return '';\n}\n\n/**\n * Parsing & Compiling\n */\n\nfunction Parser(options) {\n this.tokens = [];\n this.token = null;\n this.options = options || marked.defaults;\n this.options.renderer = this.options.renderer || new Renderer();\n this.renderer = this.options.renderer;\n this.renderer.options = this.options;\n}\n\n/**\n * Static Parse Method\n */\n\nParser.parse = function(src, options) {\n var parser = new Parser(options);\n return parser.parse(src);\n};\n\n/**\n * Parse Loop\n */\n\nParser.prototype.parse = function(src) {\n this.inline = new InlineLexer(src.links, this.options);\n // use an InlineLexer with a TextRenderer to extract pure text\n this.inlineText = new InlineLexer(\n src.links,\n merge({}, this.options, {renderer: new TextRenderer()})\n );\n this.tokens = src.reverse();\n\n var out = '';\n while (this.next()) {\n out += this.tok();\n }\n\n return out;\n};\n\n/**\n * Next Token\n */\n\nParser.prototype.next = function() {\n return this.token = this.tokens.pop();\n};\n\n/**\n * Preview Next Token\n */\n\nParser.prototype.peek = function() {\n return this.tokens[this.tokens.length - 1] || 0;\n};\n\n/**\n * Parse Text Tokens\n */\n\nParser.prototype.parseText = function() {\n var body = this.token.text;\n\n while (this.peek().type === 'text') {\n body += '\\n' + this.next().text;\n }\n\n return this.inline.output(body);\n};\n\n/**\n * Parse Current Token\n */\n\nParser.prototype.tok = function() {\n switch (this.token.type) {\n case 'space': {\n return '';\n }\n case 'hr': {\n return this.renderer.hr();\n }\n case 'heading': {\n return this.renderer.heading(\n this.inline.output(this.token.text),\n this.token.depth,\n unescape(this.inlineText.output(this.token.text)));\n }\n case 'code': {\n return this.renderer.code(this.token.text,\n this.token.lang,\n this.token.escaped);\n }\n case 'table': {\n var header = '',\n body = '',\n i,\n row,\n cell,\n j;\n\n // header\n cell = '';\n for (i = 0; i < this.token.header.length; i++) {\n cell += this.renderer.tablecell(\n this.inline.output(this.token.header[i]),\n { header: true, align: this.token.align[i] }\n );\n }\n header += this.renderer.tablerow(cell);\n\n for (i = 0; i < this.token.cells.length; i++) {\n row = this.token.cells[i];\n\n cell = '';\n for (j = 0; j < row.length; j++) {\n cell += this.renderer.tablecell(\n this.inline.output(row[j]),\n { header: false, align: this.token.align[j] }\n );\n }\n\n body += this.renderer.tablerow(cell);\n }\n return this.renderer.table(header, body);\n }\n case 'blockquote_start': {\n body = '';\n\n while (this.next().type !== 'blockquote_end') {\n body += this.tok();\n }\n\n return this.renderer.blockquote(body);\n }\n case 'list_start': {\n body = '';\n var ordered = this.token.ordered,\n start = this.token.start;\n\n while (this.next().type !== 'list_end') {\n body += this.tok();\n }\n\n return this.renderer.list(body, ordered, start);\n }\n case 'list_item_start': {\n body = '';\n\n if (this.token.task) {\n body += this.renderer.checkbox(this.token.checked);\n }\n\n while (this.next().type !== 'list_item_end') {\n body += this.token.type === 'text'\n ? this.parseText()\n : this.tok();\n }\n\n return this.renderer.listitem(body);\n }\n case 'loose_item_start': {\n body = '';\n\n while (this.next().type !== 'list_item_end') {\n body += this.tok();\n }\n\n return this.renderer.listitem(body);\n }\n case 'html': {\n // TODO parse inline content if parameter markdown=1\n return this.renderer.html(this.token.text);\n }\n case 'paragraph': {\n return this.renderer.paragraph(this.inline.output(this.token.text));\n }\n case 'text': {\n return this.renderer.paragraph(this.parseText());\n }\n }\n};\n\n/**\n * Helpers\n */\n\nfunction escape(html, encode) {\n return html\n .replace(!encode ? /&(?!#?\\w+;)/g : /&/g, '&')\n .replace(//g, '>')\n .replace(/\"/g, '"')\n .replace(/'/g, ''');\n}\n\nfunction unescape(html) {\n // explicitly match decimal, hex, and named HTML entities\n return html.replace(/&(#(?:\\d+)|(?:#x[0-9A-Fa-f]+)|(?:\\w+));?/ig, function(_, n) {\n n = n.toLowerCase();\n if (n === 'colon') return ':';\n if (n.charAt(0) === '#') {\n return n.charAt(1) === 'x'\n ? String.fromCharCode(parseInt(n.substring(2), 16))\n : String.fromCharCode(+n.substring(1));\n }\n return '';\n });\n}\n\nfunction edit(regex, opt) {\n regex = regex.source || regex;\n opt = opt || '';\n return {\n replace: function(name, val) {\n val = val.source || val;\n val = val.replace(/(^|[^\\[])\\^/g, '$1');\n regex = regex.replace(name, val);\n return this;\n },\n getRegex: function() {\n return new RegExp(regex, opt);\n }\n };\n}\n\nfunction resolveUrl(base, href) {\n if (!baseUrls[' ' + base]) {\n // we can ignore everything in base after the last slash of its path component,\n // but we might need to add _that_\n // https://tools.ietf.org/html/rfc3986#section-3\n if (/^[^:]+:\\/*[^/]*$/.test(base)) {\n baseUrls[' ' + base] = base + '/';\n } else {\n baseUrls[' ' + base] = base.replace(/[^/]*$/, '');\n }\n }\n base = baseUrls[' ' + base];\n\n if (href.slice(0, 2) === '//') {\n return base.replace(/:[\\s\\S]*/, ':') + href;\n } else if (href.charAt(0) === '/') {\n return base.replace(/(:\\/*[^/]*)[\\s\\S]*/, '$1') + href;\n } else {\n return base + href;\n }\n}\nvar baseUrls = {};\nvar originIndependentUrl = /^$|^[a-z][a-z0-9+.-]*:|^[?#]/i;\n\nfunction noop() {}\nnoop.exec = noop;\n\nfunction merge(obj) {\n var i = 1,\n target,\n key;\n\n for (; i < arguments.length; i++) {\n target = arguments[i];\n for (key in target) {\n if (Object.prototype.hasOwnProperty.call(target, key)) {\n obj[key] = target[key];\n }\n }\n }\n\n return obj;\n}\n\nfunction splitCells(tableRow, count) {\n var cells = tableRow.replace(/([^\\\\])\\|/g, '$1 |').split(/ +\\| */),\n i = 0;\n\n if (cells.length > count) {\n cells.splice(count);\n } else {\n while (cells.length < count) cells.push('');\n }\n\n for (; i < cells.length; i++) {\n cells[i] = cells[i].replace(/\\\\\\|/g, '|');\n }\n return cells;\n}\n\n/**\n * Marked\n */\n\nfunction marked(src, opt, callback) {\n // throw error in case of non string input\n if (typeof src === 'undefined' || src === null) {\n throw new Error('marked(): input parameter is undefined or null');\n }\n if (typeof src !== 'string') {\n throw new Error('marked(): input parameter is of type '\n + Object.prototype.toString.call(src) + ', string expected');\n }\n\n if (callback || typeof opt === 'function') {\n if (!callback) {\n callback = opt;\n opt = null;\n }\n\n opt = merge({}, marked.defaults, opt || {});\n\n var highlight = opt.highlight,\n tokens,\n pending,\n i = 0;\n\n try {\n tokens = Lexer.lex(src, opt)\n } catch (e) {\n return callback(e);\n }\n\n pending = tokens.length;\n\n var done = function(err) {\n if (err) {\n opt.highlight = highlight;\n return callback(err);\n }\n\n var out;\n\n try {\n out = Parser.parse(tokens, opt);\n } catch (e) {\n err = e;\n }\n\n opt.highlight = highlight;\n\n return err\n ? callback(err)\n : callback(null, out);\n };\n\n if (!highlight || highlight.length < 3) {\n return done();\n }\n\n delete opt.highlight;\n\n if (!pending) return done();\n\n for (; i < tokens.length; i++) {\n (function(token) {\n if (token.type !== 'code') {\n return --pending || done();\n }\n return highlight(token.text, token.lang, function(err, code) {\n if (err) return done(err);\n if (code == null || code === token.text) {\n return --pending || done();\n }\n token.text = code;\n token.escaped = true;\n --pending || done();\n });\n })(tokens[i]);\n }\n\n return;\n }\n try {\n if (opt) opt = merge({}, marked.defaults, opt);\n return Parser.parse(Lexer.lex(src, opt), opt);\n } catch (e) {\n e.message += '\\nPlease report this to https://github.com/markedjs/marked.';\n if ((opt || marked.defaults).silent) {\n return '

    An error occurred:

    '\n        + escape(e.message + '', true)\n        + '
    ';\n }\n throw e;\n }\n}\n\n/**\n * Options\n */\n\nmarked.options =\nmarked.setOptions = function(opt) {\n merge(marked.defaults, opt);\n return marked;\n};\n\nmarked.getDefaults = function () {\n return {\n baseUrl: null,\n breaks: false,\n gfm: true,\n headerIds: true,\n headerPrefix: '',\n highlight: null,\n langPrefix: 'language-',\n mangle: true,\n pedantic: false,\n renderer: new Renderer(),\n sanitize: false,\n sanitizer: null,\n silent: false,\n smartLists: false,\n smartypants: false,\n tables: true,\n xhtml: false\n };\n}\n\nmarked.defaults = marked.getDefaults();\n\n/**\n * Expose\n */\n\nmarked.Parser = Parser;\nmarked.parser = Parser.parse;\n\nmarked.Renderer = Renderer;\nmarked.TextRenderer = TextRenderer;\n\nmarked.Lexer = Lexer;\nmarked.lexer = Lexer.lex;\n\nmarked.InlineLexer = InlineLexer;\nmarked.inlineLexer = InlineLexer.output;\n\nmarked.parse = marked;\n\nif (typeof module !== 'undefined' && typeof exports === 'object') {\n module.exports = marked;\n} else if (typeof define === 'function' && define.amd) {\n define(function() { return marked; });\n} else {\n root.marked = marked;\n}\n})(this || (typeof window !== 'undefined' ? window : global));\n","// 7.2.2 IsArray(argument)\nvar cof = require('./_cof');\nmodule.exports = Array.isArray || function isArray(arg) {\n return cof(arg) == 'Array';\n};\n","var dP = require('./_object-dp');\nvar anObject = require('./_an-object');\nvar getKeys = require('./_object-keys');\n\nmodule.exports = require('./_descriptors') ? Object.defineProperties : function defineProperties(O, Properties) {\n anObject(O);\n var keys = getKeys(Properties);\n var length = keys.length;\n var i = 0;\n var P;\n while (length > i) dP.f(O, P = keys[i++], Properties[P]);\n return O;\n};\n","var ctx = require('./_ctx');\nvar invoke = require('./_invoke');\nvar html = require('./_html');\nvar cel = require('./_dom-create');\nvar global = require('./_global');\nvar process = global.process;\nvar setTask = global.setImmediate;\nvar clearTask = global.clearImmediate;\nvar MessageChannel = global.MessageChannel;\nvar Dispatch = global.Dispatch;\nvar counter = 0;\nvar queue = {};\nvar ONREADYSTATECHANGE = 'onreadystatechange';\nvar defer, channel, port;\nvar run = function () {\n var id = +this;\n // eslint-disable-next-line no-prototype-builtins\n if (queue.hasOwnProperty(id)) {\n var fn = queue[id];\n delete queue[id];\n fn();\n }\n};\nvar listener = function (event) {\n run.call(event.data);\n};\n// Node.js 0.9+ & IE10+ has setImmediate, otherwise:\nif (!setTask || !clearTask) {\n setTask = function setImmediate(fn) {\n var args = [];\n var i = 1;\n while (arguments.length > i) args.push(arguments[i++]);\n queue[++counter] = function () {\n // eslint-disable-next-line no-new-func\n invoke(typeof fn == 'function' ? fn : Function(fn), args);\n };\n defer(counter);\n return counter;\n };\n clearTask = function clearImmediate(id) {\n delete queue[id];\n };\n // Node.js 0.8-\n if (require('./_cof')(process) == 'process') {\n defer = function (id) {\n process.nextTick(ctx(run, id, 1));\n };\n // Sphere (JS game engine) Dispatch API\n } else if (Dispatch && Dispatch.now) {\n defer = function (id) {\n Dispatch.now(ctx(run, id, 1));\n };\n // Browsers with MessageChannel, includes WebWorkers\n } else if (MessageChannel) {\n channel = new MessageChannel();\n port = channel.port2;\n channel.port1.onmessage = listener;\n defer = ctx(port.postMessage, port, 1);\n // Browsers with postMessage, skip WebWorkers\n // IE8 has postMessage, but it's sync & typeof its postMessage is 'object'\n } else if (global.addEventListener && typeof postMessage == 'function' && !global.importScripts) {\n defer = function (id) {\n global.postMessage(id + '', '*');\n };\n global.addEventListener('message', listener, false);\n // IE8-\n } else if (ONREADYSTATECHANGE in cel('script')) {\n defer = function (id) {\n html.appendChild(cel('script'))[ONREADYSTATECHANGE] = function () {\n html.removeChild(this);\n run.call(id);\n };\n };\n // Rest old browsers\n } else {\n defer = function (id) {\n setTimeout(ctx(run, id, 1), 0);\n };\n }\n}\nmodule.exports = {\n set: setTask,\n clear: clearTask\n};\n","// call something on iterator step with safe closing on error\nvar anObject = require('./_an-object');\nmodule.exports = function (iterator, fn, value, entries) {\n try {\n return entries ? fn(anObject(value)[0], value[1]) : fn(value);\n // 7.4.6 IteratorClose(iterator, completion)\n } catch (e) {\n var ret = iterator['return'];\n if (ret !== undefined) anObject(ret.call(iterator));\n throw e;\n }\n};\n","// getting tag from 19.1.3.6 Object.prototype.toString()\nvar cof = require('./_cof');\nvar TAG = require('./_wks')('toStringTag');\n// ES3 wrong here\nvar ARG = cof(function () { return arguments; }()) == 'Arguments';\n\n// fallback for IE11 Script Access Denied error\nvar tryGet = function (it, key) {\n try {\n return it[key];\n } catch (e) { /* empty */ }\n};\n\nmodule.exports = function (it) {\n var O, T, B;\n return it === undefined ? 'Undefined' : it === null ? 'Null'\n // @@toStringTag case\n : typeof (T = tryGet(O = Object(it), TAG)) == 'string' ? T\n // builtinTag case\n : ARG ? cof(O)\n // ES3 arguments fallback\n : (B = cof(O)) == 'Object' && typeof O.callee == 'function' ? 'Arguments' : B;\n};\n","'use strict';\n// 22.1.3.9 Array.prototype.findIndex(predicate, thisArg = undefined)\nvar $export = require('./_export');\nvar $find = require('./_array-methods')(6);\nvar KEY = 'findIndex';\nvar forced = true;\n// Shouldn't skip holes\nif (KEY in []) Array(1)[KEY](function () { forced = false; });\n$export($export.P + $export.F * forced, 'Array', {\n findIndex: function findIndex(callbackfn /* , that = undefined */) {\n return $find(this, callbackfn, arguments.length > 1 ? arguments[1] : undefined);\n }\n});\nrequire('./_add-to-unscopables')(KEY);\n","var isObject = require('./_is-object');\nvar document = require('./_global').document;\n// typeof document.createElement is 'object' in old IE\nvar is = isObject(document) && isObject(document.createElement);\nmodule.exports = function (it) {\n return is ? document.createElement(it) : {};\n};\n","var classof = require('./_classof');\nvar ITERATOR = require('./_wks')('iterator');\nvar Iterators = require('./_iterators');\nmodule.exports = require('./_core').getIteratorMethod = function (it) {\n if (it != undefined) return it[ITERATOR]\n || it['@@iterator']\n || Iterators[classof(it)];\n};\n","exports.f = Object.getOwnPropertySymbols;\n","var store = require('./_shared')('wks');\nvar uid = require('./_uid');\nvar Symbol = require('./_global').Symbol;\nvar USE_SYMBOL = typeof Symbol == 'function';\n\nvar $exports = module.exports = function (name) {\n return store[name] || (store[name] =\n USE_SYMBOL && Symbol[name] || (USE_SYMBOL ? Symbol : uid)('Symbol.' + name));\n};\n\n$exports.store = store;\n","/* globals __VUE_SSR_CONTEXT__ */\n\n// IMPORTANT: Do NOT use ES2015 features in this file (except for modules).\n// This module is a runtime utility for cleaner component module output and will\n// be included in the final webpack user bundle.\n\nexport default function normalizeComponent (\n scriptExports,\n render,\n staticRenderFns,\n functionalTemplate,\n injectStyles,\n scopeId,\n moduleIdentifier, /* server only */\n shadowMode /* vue-cli only */\n) {\n // Vue.extend constructor export interop\n var options = typeof scriptExports === 'function'\n ? scriptExports.options\n : scriptExports\n\n // render functions\n if (render) {\n options.render = render\n options.staticRenderFns = staticRenderFns\n options._compiled = true\n }\n\n // functional template\n if (functionalTemplate) {\n options.functional = true\n }\n\n // scopedId\n if (scopeId) {\n options._scopeId = 'data-v-' + scopeId\n }\n\n var hook\n if (moduleIdentifier) { // server build\n hook = function (context) {\n // 2.3 injection\n context =\n context || // cached call\n (this.$vnode && this.$vnode.ssrContext) || // stateful\n (this.parent && this.parent.$vnode && this.parent.$vnode.ssrContext) // functional\n // 2.2 with runInNewContext: true\n if (!context && typeof __VUE_SSR_CONTEXT__ !== 'undefined') {\n context = __VUE_SSR_CONTEXT__\n }\n // inject component styles\n if (injectStyles) {\n injectStyles.call(this, context)\n }\n // register component module identifier for async chunk inferrence\n if (context && context._registeredComponents) {\n context._registeredComponents.add(moduleIdentifier)\n }\n }\n // used by ssr in case component is cached and beforeCreate\n // never gets called\n options._ssrRegister = hook\n } else if (injectStyles) {\n hook = shadowMode\n ? function () { injectStyles.call(this, this.$root.$options.shadowRoot) }\n : injectStyles\n }\n\n if (hook) {\n if (options.functional) {\n // for template-only hot-reload because in that case the render fn doesn't\n // go through the normalizer\n options._injectStyles = hook\n // register for functioal component in vue file\n var originalRender = options.render\n options.render = function renderWithStyleInjection (h, context) {\n hook.call(context)\n return originalRender(h, context)\n }\n } else {\n // inject component registration as beforeCreate hook\n var existing = options.beforeCreate\n options.beforeCreate = existing\n ? [].concat(existing, hook)\n : [hook]\n }\n }\n\n return {\n exports: scriptExports,\n options: options\n }\n}\n","var global = require('./_global');\nvar hide = require('./_hide');\nvar has = require('./_has');\nvar SRC = require('./_uid')('src');\nvar TO_STRING = 'toString';\nvar $toString = Function[TO_STRING];\nvar TPL = ('' + $toString).split(TO_STRING);\n\nrequire('./_core').inspectSource = function (it) {\n return $toString.call(it);\n};\n\n(module.exports = function (O, key, val, safe) {\n var isFunction = typeof val == 'function';\n if (isFunction) has(val, 'name') || hide(val, 'name', key);\n if (O[key] === val) return;\n if (isFunction) has(val, SRC) || hide(val, SRC, O[key] ? '' + O[key] : TPL.join(String(key)));\n if (O === global) {\n O[key] = val;\n } else if (!safe) {\n delete O[key];\n hide(O, key, val);\n } else if (O[key]) {\n O[key] = val;\n } else {\n hide(O, key, val);\n }\n// add fake Function#toString for correct work wrapped methods / constructors with methods like LoDash isNative\n})(Function.prototype, TO_STRING, function toString() {\n return typeof this == 'function' && this[SRC] || $toString.call(this);\n});\n","// 19.1.2.2 / 15.2.3.5 Object.create(O [, Properties])\nvar anObject = require('./_an-object');\nvar dPs = require('./_object-dps');\nvar enumBugKeys = require('./_enum-bug-keys');\nvar IE_PROTO = require('./_shared-key')('IE_PROTO');\nvar Empty = function () { /* empty */ };\nvar PROTOTYPE = 'prototype';\n\n// Create object with fake `null` prototype: use iframe Object with cleared prototype\nvar createDict = function () {\n // Thrash, waste and sodomy: IE GC bug\n var iframe = require('./_dom-create')('iframe');\n var i = enumBugKeys.length;\n var lt = '<';\n var gt = '>';\n var iframeDocument;\n iframe.style.display = 'none';\n require('./_html').appendChild(iframe);\n iframe.src = 'javascript:'; // eslint-disable-line no-script-url\n // createDict = iframe.contentWindow.Object;\n // html.removeChild(iframe);\n iframeDocument = iframe.contentWindow.document;\n iframeDocument.open();\n iframeDocument.write(lt + 'script' + gt + 'document.F=Object' + lt + '/script' + gt);\n iframeDocument.close();\n createDict = iframeDocument.F;\n while (i--) delete createDict[PROTOTYPE][enumBugKeys[i]];\n return createDict();\n};\n\nmodule.exports = Object.create || function create(O, Properties) {\n var result;\n if (O !== null) {\n Empty[PROTOTYPE] = anObject(O);\n result = new Empty();\n Empty[PROTOTYPE] = null;\n // add \"__proto__\" for Object.getPrototypeOf polyfill\n result[IE_PROTO] = O;\n } else result = createDict();\n return Properties === undefined ? result : dPs(result, Properties);\n};\n","/*!\n * Vue.js v2.5.16\n * (c) 2014-2018 Evan You\n * Released under the MIT License.\n */\n/* */\n\nvar emptyObject = Object.freeze({});\n\n// these helpers produces better vm code in JS engines due to their\n// explicitness and function inlining\nfunction isUndef (v) {\n return v === undefined || v === null\n}\n\nfunction isDef (v) {\n return v !== undefined && v !== null\n}\n\nfunction isTrue (v) {\n return v === true\n}\n\nfunction isFalse (v) {\n return v === false\n}\n\n/**\n * Check if value is primitive\n */\nfunction isPrimitive (value) {\n return (\n typeof value === 'string' ||\n typeof value === 'number' ||\n // $flow-disable-line\n typeof value === 'symbol' ||\n typeof value === 'boolean'\n )\n}\n\n/**\n * Quick object check - this is primarily used to tell\n * Objects from primitive values when we know the value\n * is a JSON-compliant type.\n */\nfunction isObject (obj) {\n return obj !== null && typeof obj === 'object'\n}\n\n/**\n * Get the raw type string of a value e.g. [object Object]\n */\nvar _toString = Object.prototype.toString;\n\nfunction toRawType (value) {\n return _toString.call(value).slice(8, -1)\n}\n\n/**\n * Strict object type check. Only returns true\n * for plain JavaScript objects.\n */\nfunction isPlainObject (obj) {\n return _toString.call(obj) === '[object Object]'\n}\n\nfunction isRegExp (v) {\n return _toString.call(v) === '[object RegExp]'\n}\n\n/**\n * Check if val is a valid array index.\n */\nfunction isValidArrayIndex (val) {\n var n = parseFloat(String(val));\n return n >= 0 && Math.floor(n) === n && isFinite(val)\n}\n\n/**\n * Convert a value to a string that is actually rendered.\n */\nfunction toString (val) {\n return val == null\n ? ''\n : typeof val === 'object'\n ? JSON.stringify(val, null, 2)\n : String(val)\n}\n\n/**\n * Convert a input value to a number for persistence.\n * If the conversion fails, return original string.\n */\nfunction toNumber (val) {\n var n = parseFloat(val);\n return isNaN(n) ? val : n\n}\n\n/**\n * Make a map and return a function for checking if a key\n * is in that map.\n */\nfunction makeMap (\n str,\n expectsLowerCase\n) {\n var map = Object.create(null);\n var list = str.split(',');\n for (var i = 0; i < list.length; i++) {\n map[list[i]] = true;\n }\n return expectsLowerCase\n ? function (val) { return map[val.toLowerCase()]; }\n : function (val) { return map[val]; }\n}\n\n/**\n * Check if a tag is a built-in tag.\n */\nvar isBuiltInTag = makeMap('slot,component', true);\n\n/**\n * Check if a attribute is a reserved attribute.\n */\nvar isReservedAttribute = makeMap('key,ref,slot,slot-scope,is');\n\n/**\n * Remove an item from an array\n */\nfunction remove (arr, item) {\n if (arr.length) {\n var index = arr.indexOf(item);\n if (index > -1) {\n return arr.splice(index, 1)\n }\n }\n}\n\n/**\n * Check whether the object has the property.\n */\nvar hasOwnProperty = Object.prototype.hasOwnProperty;\nfunction hasOwn (obj, key) {\n return hasOwnProperty.call(obj, key)\n}\n\n/**\n * Create a cached version of a pure function.\n */\nfunction cached (fn) {\n var cache = Object.create(null);\n return (function cachedFn (str) {\n var hit = cache[str];\n return hit || (cache[str] = fn(str))\n })\n}\n\n/**\n * Camelize a hyphen-delimited string.\n */\nvar camelizeRE = /-(\\w)/g;\nvar camelize = cached(function (str) {\n return str.replace(camelizeRE, function (_, c) { return c ? c.toUpperCase() : ''; })\n});\n\n/**\n * Capitalize a string.\n */\nvar capitalize = cached(function (str) {\n return str.charAt(0).toUpperCase() + str.slice(1)\n});\n\n/**\n * Hyphenate a camelCase string.\n */\nvar hyphenateRE = /\\B([A-Z])/g;\nvar hyphenate = cached(function (str) {\n return str.replace(hyphenateRE, '-$1').toLowerCase()\n});\n\n/**\n * Simple bind polyfill for environments that do not support it... e.g.\n * PhantomJS 1.x. Technically we don't need this anymore since native bind is\n * now more performant in most browsers, but removing it would be breaking for\n * code that was able to run in PhantomJS 1.x, so this must be kept for\n * backwards compatibility.\n */\n\n/* istanbul ignore next */\nfunction polyfillBind (fn, ctx) {\n function boundFn (a) {\n var l = arguments.length;\n return l\n ? l > 1\n ? fn.apply(ctx, arguments)\n : fn.call(ctx, a)\n : fn.call(ctx)\n }\n\n boundFn._length = fn.length;\n return boundFn\n}\n\nfunction nativeBind (fn, ctx) {\n return fn.bind(ctx)\n}\n\nvar bind = Function.prototype.bind\n ? nativeBind\n : polyfillBind;\n\n/**\n * Convert an Array-like object to a real Array.\n */\nfunction toArray (list, start) {\n start = start || 0;\n var i = list.length - start;\n var ret = new Array(i);\n while (i--) {\n ret[i] = list[i + start];\n }\n return ret\n}\n\n/**\n * Mix properties into target object.\n */\nfunction extend (to, _from) {\n for (var key in _from) {\n to[key] = _from[key];\n }\n return to\n}\n\n/**\n * Merge an Array of Objects into a single Object.\n */\nfunction toObject (arr) {\n var res = {};\n for (var i = 0; i < arr.length; i++) {\n if (arr[i]) {\n extend(res, arr[i]);\n }\n }\n return res\n}\n\n/**\n * Perform no operation.\n * Stubbing args to make Flow happy without leaving useless transpiled code\n * with ...rest (https://flow.org/blog/2017/05/07/Strict-Function-Call-Arity/)\n */\nfunction noop (a, b, c) {}\n\n/**\n * Always return false.\n */\nvar no = function (a, b, c) { return false; };\n\n/**\n * Return same value\n */\nvar identity = function (_) { return _; };\n\n/**\n * Generate a static keys string from compiler modules.\n */\n\n\n/**\n * Check if two values are loosely equal - that is,\n * if they are plain objects, do they have the same shape?\n */\nfunction looseEqual (a, b) {\n if (a === b) { return true }\n var isObjectA = isObject(a);\n var isObjectB = isObject(b);\n if (isObjectA && isObjectB) {\n try {\n var isArrayA = Array.isArray(a);\n var isArrayB = Array.isArray(b);\n if (isArrayA && isArrayB) {\n return a.length === b.length && a.every(function (e, i) {\n return looseEqual(e, b[i])\n })\n } else if (!isArrayA && !isArrayB) {\n var keysA = Object.keys(a);\n var keysB = Object.keys(b);\n return keysA.length === keysB.length && keysA.every(function (key) {\n return looseEqual(a[key], b[key])\n })\n } else {\n /* istanbul ignore next */\n return false\n }\n } catch (e) {\n /* istanbul ignore next */\n return false\n }\n } else if (!isObjectA && !isObjectB) {\n return String(a) === String(b)\n } else {\n return false\n }\n}\n\nfunction looseIndexOf (arr, val) {\n for (var i = 0; i < arr.length; i++) {\n if (looseEqual(arr[i], val)) { return i }\n }\n return -1\n}\n\n/**\n * Ensure a function is called only once.\n */\nfunction once (fn) {\n var called = false;\n return function () {\n if (!called) {\n called = true;\n fn.apply(this, arguments);\n }\n }\n}\n\nvar SSR_ATTR = 'data-server-rendered';\n\nvar ASSET_TYPES = [\n 'component',\n 'directive',\n 'filter'\n];\n\nvar LIFECYCLE_HOOKS = [\n 'beforeCreate',\n 'created',\n 'beforeMount',\n 'mounted',\n 'beforeUpdate',\n 'updated',\n 'beforeDestroy',\n 'destroyed',\n 'activated',\n 'deactivated',\n 'errorCaptured'\n];\n\n/* */\n\nvar config = ({\n /**\n * Option merge strategies (used in core/util/options)\n */\n // $flow-disable-line\n optionMergeStrategies: Object.create(null),\n\n /**\n * Whether to suppress warnings.\n */\n silent: false,\n\n /**\n * Show production mode tip message on boot?\n */\n productionTip: process.env.NODE_ENV !== 'production',\n\n /**\n * Whether to enable devtools\n */\n devtools: process.env.NODE_ENV !== 'production',\n\n /**\n * Whether to record perf\n */\n performance: false,\n\n /**\n * Error handler for watcher errors\n */\n errorHandler: null,\n\n /**\n * Warn handler for watcher warns\n */\n warnHandler: null,\n\n /**\n * Ignore certain custom elements\n */\n ignoredElements: [],\n\n /**\n * Custom user key aliases for v-on\n */\n // $flow-disable-line\n keyCodes: Object.create(null),\n\n /**\n * Check if a tag is reserved so that it cannot be registered as a\n * component. This is platform-dependent and may be overwritten.\n */\n isReservedTag: no,\n\n /**\n * Check if an attribute is reserved so that it cannot be used as a component\n * prop. This is platform-dependent and may be overwritten.\n */\n isReservedAttr: no,\n\n /**\n * Check if a tag is an unknown element.\n * Platform-dependent.\n */\n isUnknownElement: no,\n\n /**\n * Get the namespace of an element\n */\n getTagNamespace: noop,\n\n /**\n * Parse the real tag name for the specific platform.\n */\n parsePlatformTagName: identity,\n\n /**\n * Check if an attribute must be bound using property, e.g. value\n * Platform-dependent.\n */\n mustUseProp: no,\n\n /**\n * Exposed for legacy reasons\n */\n _lifecycleHooks: LIFECYCLE_HOOKS\n})\n\n/* */\n\n/**\n * Check if a string starts with $ or _\n */\nfunction isReserved (str) {\n var c = (str + '').charCodeAt(0);\n return c === 0x24 || c === 0x5F\n}\n\n/**\n * Define a property.\n */\nfunction def (obj, key, val, enumerable) {\n Object.defineProperty(obj, key, {\n value: val,\n enumerable: !!enumerable,\n writable: true,\n configurable: true\n });\n}\n\n/**\n * Parse simple path.\n */\nvar bailRE = /[^\\w.$]/;\nfunction parsePath (path) {\n if (bailRE.test(path)) {\n return\n }\n var segments = path.split('.');\n return function (obj) {\n for (var i = 0; i < segments.length; i++) {\n if (!obj) { return }\n obj = obj[segments[i]];\n }\n return obj\n }\n}\n\n/* */\n\n// can we use __proto__?\nvar hasProto = '__proto__' in {};\n\n// Browser environment sniffing\nvar inBrowser = typeof window !== 'undefined';\nvar inWeex = typeof WXEnvironment !== 'undefined' && !!WXEnvironment.platform;\nvar weexPlatform = inWeex && WXEnvironment.platform.toLowerCase();\nvar UA = inBrowser && window.navigator.userAgent.toLowerCase();\nvar isIE = UA && /msie|trident/.test(UA);\nvar isIE9 = UA && UA.indexOf('msie 9.0') > 0;\nvar isEdge = UA && UA.indexOf('edge/') > 0;\nvar isAndroid = (UA && UA.indexOf('android') > 0) || (weexPlatform === 'android');\nvar isIOS = (UA && /iphone|ipad|ipod|ios/.test(UA)) || (weexPlatform === 'ios');\nvar isChrome = UA && /chrome\\/\\d+/.test(UA) && !isEdge;\n\n// Firefox has a \"watch\" function on Object.prototype...\nvar nativeWatch = ({}).watch;\n\nvar supportsPassive = false;\nif (inBrowser) {\n try {\n var opts = {};\n Object.defineProperty(opts, 'passive', ({\n get: function get () {\n /* istanbul ignore next */\n supportsPassive = true;\n }\n })); // https://github.com/facebook/flow/issues/285\n window.addEventListener('test-passive', null, opts);\n } catch (e) {}\n}\n\n// this needs to be lazy-evaled because vue may be required before\n// vue-server-renderer can set VUE_ENV\nvar _isServer;\nvar isServerRendering = function () {\n if (_isServer === undefined) {\n /* istanbul ignore if */\n if (!inBrowser && !inWeex && typeof global !== 'undefined') {\n // detect presence of vue-server-renderer and avoid\n // Webpack shimming the process\n _isServer = global['process'].env.VUE_ENV === 'server';\n } else {\n _isServer = false;\n }\n }\n return _isServer\n};\n\n// detect devtools\nvar devtools = inBrowser && window.__VUE_DEVTOOLS_GLOBAL_HOOK__;\n\n/* istanbul ignore next */\nfunction isNative (Ctor) {\n return typeof Ctor === 'function' && /native code/.test(Ctor.toString())\n}\n\nvar hasSymbol =\n typeof Symbol !== 'undefined' && isNative(Symbol) &&\n typeof Reflect !== 'undefined' && isNative(Reflect.ownKeys);\n\nvar _Set;\n/* istanbul ignore if */ // $flow-disable-line\nif (typeof Set !== 'undefined' && isNative(Set)) {\n // use native Set when available.\n _Set = Set;\n} else {\n // a non-standard Set polyfill that only works with primitive keys.\n _Set = (function () {\n function Set () {\n this.set = Object.create(null);\n }\n Set.prototype.has = function has (key) {\n return this.set[key] === true\n };\n Set.prototype.add = function add (key) {\n this.set[key] = true;\n };\n Set.prototype.clear = function clear () {\n this.set = Object.create(null);\n };\n\n return Set;\n }());\n}\n\n/* */\n\nvar warn = noop;\nvar tip = noop;\nvar generateComponentTrace = (noop); // work around flow check\nvar formatComponentName = (noop);\n\nif (process.env.NODE_ENV !== 'production') {\n var hasConsole = typeof console !== 'undefined';\n var classifyRE = /(?:^|[-_])(\\w)/g;\n var classify = function (str) { return str\n .replace(classifyRE, function (c) { return c.toUpperCase(); })\n .replace(/[-_]/g, ''); };\n\n warn = function (msg, vm) {\n var trace = vm ? generateComponentTrace(vm) : '';\n\n if (config.warnHandler) {\n config.warnHandler.call(null, msg, vm, trace);\n } else if (hasConsole && (!config.silent)) {\n console.error((\"[Vue warn]: \" + msg + trace));\n }\n };\n\n tip = function (msg, vm) {\n if (hasConsole && (!config.silent)) {\n console.warn(\"[Vue tip]: \" + msg + (\n vm ? generateComponentTrace(vm) : ''\n ));\n }\n };\n\n formatComponentName = function (vm, includeFile) {\n if (vm.$root === vm) {\n return ''\n }\n var options = typeof vm === 'function' && vm.cid != null\n ? vm.options\n : vm._isVue\n ? vm.$options || vm.constructor.options\n : vm || {};\n var name = options.name || options._componentTag;\n var file = options.__file;\n if (!name && file) {\n var match = file.match(/([^/\\\\]+)\\.vue$/);\n name = match && match[1];\n }\n\n return (\n (name ? (\"<\" + (classify(name)) + \">\") : \"\") +\n (file && includeFile !== false ? (\" at \" + file) : '')\n )\n };\n\n var repeat = function (str, n) {\n var res = '';\n while (n) {\n if (n % 2 === 1) { res += str; }\n if (n > 1) { str += str; }\n n >>= 1;\n }\n return res\n };\n\n generateComponentTrace = function (vm) {\n if (vm._isVue && vm.$parent) {\n var tree = [];\n var currentRecursiveSequence = 0;\n while (vm) {\n if (tree.length > 0) {\n var last = tree[tree.length - 1];\n if (last.constructor === vm.constructor) {\n currentRecursiveSequence++;\n vm = vm.$parent;\n continue\n } else if (currentRecursiveSequence > 0) {\n tree[tree.length - 1] = [last, currentRecursiveSequence];\n currentRecursiveSequence = 0;\n }\n }\n tree.push(vm);\n vm = vm.$parent;\n }\n return '\\n\\nfound in\\n\\n' + tree\n .map(function (vm, i) { return (\"\" + (i === 0 ? '---> ' : repeat(' ', 5 + i * 2)) + (Array.isArray(vm)\n ? ((formatComponentName(vm[0])) + \"... (\" + (vm[1]) + \" recursive calls)\")\n : formatComponentName(vm))); })\n .join('\\n')\n } else {\n return (\"\\n\\n(found in \" + (formatComponentName(vm)) + \")\")\n }\n };\n}\n\n/* */\n\n\nvar uid = 0;\n\n/**\n * A dep is an observable that can have multiple\n * directives subscribing to it.\n */\nvar Dep = function Dep () {\n this.id = uid++;\n this.subs = [];\n};\n\nDep.prototype.addSub = function addSub (sub) {\n this.subs.push(sub);\n};\n\nDep.prototype.removeSub = function removeSub (sub) {\n remove(this.subs, sub);\n};\n\nDep.prototype.depend = function depend () {\n if (Dep.target) {\n Dep.target.addDep(this);\n }\n};\n\nDep.prototype.notify = function notify () {\n // stabilize the subscriber list first\n var subs = this.subs.slice();\n for (var i = 0, l = subs.length; i < l; i++) {\n subs[i].update();\n }\n};\n\n// the current target watcher being evaluated.\n// this is globally unique because there could be only one\n// watcher being evaluated at any time.\nDep.target = null;\nvar targetStack = [];\n\nfunction pushTarget (_target) {\n if (Dep.target) { targetStack.push(Dep.target); }\n Dep.target = _target;\n}\n\nfunction popTarget () {\n Dep.target = targetStack.pop();\n}\n\n/* */\n\nvar VNode = function VNode (\n tag,\n data,\n children,\n text,\n elm,\n context,\n componentOptions,\n asyncFactory\n) {\n this.tag = tag;\n this.data = data;\n this.children = children;\n this.text = text;\n this.elm = elm;\n this.ns = undefined;\n this.context = context;\n this.fnContext = undefined;\n this.fnOptions = undefined;\n this.fnScopeId = undefined;\n this.key = data && data.key;\n this.componentOptions = componentOptions;\n this.componentInstance = undefined;\n this.parent = undefined;\n this.raw = false;\n this.isStatic = false;\n this.isRootInsert = true;\n this.isComment = false;\n this.isCloned = false;\n this.isOnce = false;\n this.asyncFactory = asyncFactory;\n this.asyncMeta = undefined;\n this.isAsyncPlaceholder = false;\n};\n\nvar prototypeAccessors = { child: { configurable: true } };\n\n// DEPRECATED: alias for componentInstance for backwards compat.\n/* istanbul ignore next */\nprototypeAccessors.child.get = function () {\n return this.componentInstance\n};\n\nObject.defineProperties( VNode.prototype, prototypeAccessors );\n\nvar createEmptyVNode = function (text) {\n if ( text === void 0 ) text = '';\n\n var node = new VNode();\n node.text = text;\n node.isComment = true;\n return node\n};\n\nfunction createTextVNode (val) {\n return new VNode(undefined, undefined, undefined, String(val))\n}\n\n// optimized shallow clone\n// used for static nodes and slot nodes because they may be reused across\n// multiple renders, cloning them avoids errors when DOM manipulations rely\n// on their elm reference.\nfunction cloneVNode (vnode) {\n var cloned = new VNode(\n vnode.tag,\n vnode.data,\n vnode.children,\n vnode.text,\n vnode.elm,\n vnode.context,\n vnode.componentOptions,\n vnode.asyncFactory\n );\n cloned.ns = vnode.ns;\n cloned.isStatic = vnode.isStatic;\n cloned.key = vnode.key;\n cloned.isComment = vnode.isComment;\n cloned.fnContext = vnode.fnContext;\n cloned.fnOptions = vnode.fnOptions;\n cloned.fnScopeId = vnode.fnScopeId;\n cloned.isCloned = true;\n return cloned\n}\n\n/*\n * not type checking this file because flow doesn't play well with\n * dynamically accessing methods on Array prototype\n */\n\nvar arrayProto = Array.prototype;\nvar arrayMethods = Object.create(arrayProto);\n\nvar methodsToPatch = [\n 'push',\n 'pop',\n 'shift',\n 'unshift',\n 'splice',\n 'sort',\n 'reverse'\n];\n\n/**\n * Intercept mutating methods and emit events\n */\nmethodsToPatch.forEach(function (method) {\n // cache original method\n var original = arrayProto[method];\n def(arrayMethods, method, function mutator () {\n var args = [], len = arguments.length;\n while ( len-- ) args[ len ] = arguments[ len ];\n\n var result = original.apply(this, args);\n var ob = this.__ob__;\n var inserted;\n switch (method) {\n case 'push':\n case 'unshift':\n inserted = args;\n break\n case 'splice':\n inserted = args.slice(2);\n break\n }\n if (inserted) { ob.observeArray(inserted); }\n // notify change\n ob.dep.notify();\n return result\n });\n});\n\n/* */\n\nvar arrayKeys = Object.getOwnPropertyNames(arrayMethods);\n\n/**\n * In some cases we may want to disable observation inside a component's\n * update computation.\n */\nvar shouldObserve = true;\n\nfunction toggleObserving (value) {\n shouldObserve = value;\n}\n\n/**\n * Observer class that is attached to each observed\n * object. Once attached, the observer converts the target\n * object's property keys into getter/setters that\n * collect dependencies and dispatch updates.\n */\nvar Observer = function Observer (value) {\n this.value = value;\n this.dep = new Dep();\n this.vmCount = 0;\n def(value, '__ob__', this);\n if (Array.isArray(value)) {\n var augment = hasProto\n ? protoAugment\n : copyAugment;\n augment(value, arrayMethods, arrayKeys);\n this.observeArray(value);\n } else {\n this.walk(value);\n }\n};\n\n/**\n * Walk through each property and convert them into\n * getter/setters. This method should only be called when\n * value type is Object.\n */\nObserver.prototype.walk = function walk (obj) {\n var keys = Object.keys(obj);\n for (var i = 0; i < keys.length; i++) {\n defineReactive(obj, keys[i]);\n }\n};\n\n/**\n * Observe a list of Array items.\n */\nObserver.prototype.observeArray = function observeArray (items) {\n for (var i = 0, l = items.length; i < l; i++) {\n observe(items[i]);\n }\n};\n\n// helpers\n\n/**\n * Augment an target Object or Array by intercepting\n * the prototype chain using __proto__\n */\nfunction protoAugment (target, src, keys) {\n /* eslint-disable no-proto */\n target.__proto__ = src;\n /* eslint-enable no-proto */\n}\n\n/**\n * Augment an target Object or Array by defining\n * hidden properties.\n */\n/* istanbul ignore next */\nfunction copyAugment (target, src, keys) {\n for (var i = 0, l = keys.length; i < l; i++) {\n var key = keys[i];\n def(target, key, src[key]);\n }\n}\n\n/**\n * Attempt to create an observer instance for a value,\n * returns the new observer if successfully observed,\n * or the existing observer if the value already has one.\n */\nfunction observe (value, asRootData) {\n if (!isObject(value) || value instanceof VNode) {\n return\n }\n var ob;\n if (hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) {\n ob = value.__ob__;\n } else if (\n shouldObserve &&\n !isServerRendering() &&\n (Array.isArray(value) || isPlainObject(value)) &&\n Object.isExtensible(value) &&\n !value._isVue\n ) {\n ob = new Observer(value);\n }\n if (asRootData && ob) {\n ob.vmCount++;\n }\n return ob\n}\n\n/**\n * Define a reactive property on an Object.\n */\nfunction defineReactive (\n obj,\n key,\n val,\n customSetter,\n shallow\n) {\n var dep = new Dep();\n\n var property = Object.getOwnPropertyDescriptor(obj, key);\n if (property && property.configurable === false) {\n return\n }\n\n // cater for pre-defined getter/setters\n var getter = property && property.get;\n if (!getter && arguments.length === 2) {\n val = obj[key];\n }\n var setter = property && property.set;\n\n var childOb = !shallow && observe(val);\n Object.defineProperty(obj, key, {\n enumerable: true,\n configurable: true,\n get: function reactiveGetter () {\n var value = getter ? getter.call(obj) : val;\n if (Dep.target) {\n dep.depend();\n if (childOb) {\n childOb.dep.depend();\n if (Array.isArray(value)) {\n dependArray(value);\n }\n }\n }\n return value\n },\n set: function reactiveSetter (newVal) {\n var value = getter ? getter.call(obj) : val;\n /* eslint-disable no-self-compare */\n if (newVal === value || (newVal !== newVal && value !== value)) {\n return\n }\n /* eslint-enable no-self-compare */\n if (process.env.NODE_ENV !== 'production' && customSetter) {\n customSetter();\n }\n if (setter) {\n setter.call(obj, newVal);\n } else {\n val = newVal;\n }\n childOb = !shallow && observe(newVal);\n dep.notify();\n }\n });\n}\n\n/**\n * Set a property on an object. Adds the new property and\n * triggers change notification if the property doesn't\n * already exist.\n */\nfunction set (target, key, val) {\n if (process.env.NODE_ENV !== 'production' &&\n (isUndef(target) || isPrimitive(target))\n ) {\n warn((\"Cannot set reactive property on undefined, null, or primitive value: \" + ((target))));\n }\n if (Array.isArray(target) && isValidArrayIndex(key)) {\n target.length = Math.max(target.length, key);\n target.splice(key, 1, val);\n return val\n }\n if (key in target && !(key in Object.prototype)) {\n target[key] = val;\n return val\n }\n var ob = (target).__ob__;\n if (target._isVue || (ob && ob.vmCount)) {\n process.env.NODE_ENV !== 'production' && warn(\n 'Avoid adding reactive properties to a Vue instance or its root $data ' +\n 'at runtime - declare it upfront in the data option.'\n );\n return val\n }\n if (!ob) {\n target[key] = val;\n return val\n }\n defineReactive(ob.value, key, val);\n ob.dep.notify();\n return val\n}\n\n/**\n * Delete a property and trigger change if necessary.\n */\nfunction del (target, key) {\n if (process.env.NODE_ENV !== 'production' &&\n (isUndef(target) || isPrimitive(target))\n ) {\n warn((\"Cannot delete reactive property on undefined, null, or primitive value: \" + ((target))));\n }\n if (Array.isArray(target) && isValidArrayIndex(key)) {\n target.splice(key, 1);\n return\n }\n var ob = (target).__ob__;\n if (target._isVue || (ob && ob.vmCount)) {\n process.env.NODE_ENV !== 'production' && warn(\n 'Avoid deleting properties on a Vue instance or its root $data ' +\n '- just set it to null.'\n );\n return\n }\n if (!hasOwn(target, key)) {\n return\n }\n delete target[key];\n if (!ob) {\n return\n }\n ob.dep.notify();\n}\n\n/**\n * Collect dependencies on array elements when the array is touched, since\n * we cannot intercept array element access like property getters.\n */\nfunction dependArray (value) {\n for (var e = (void 0), i = 0, l = value.length; i < l; i++) {\n e = value[i];\n e && e.__ob__ && e.__ob__.dep.depend();\n if (Array.isArray(e)) {\n dependArray(e);\n }\n }\n}\n\n/* */\n\n/**\n * Option overwriting strategies are functions that handle\n * how to merge a parent option value and a child option\n * value into the final value.\n */\nvar strats = config.optionMergeStrategies;\n\n/**\n * Options with restrictions\n */\nif (process.env.NODE_ENV !== 'production') {\n strats.el = strats.propsData = function (parent, child, vm, key) {\n if (!vm) {\n warn(\n \"option \\\"\" + key + \"\\\" can only be used during instance \" +\n 'creation with the `new` keyword.'\n );\n }\n return defaultStrat(parent, child)\n };\n}\n\n/**\n * Helper that recursively merges two data objects together.\n */\nfunction mergeData (to, from) {\n if (!from) { return to }\n var key, toVal, fromVal;\n var keys = Object.keys(from);\n for (var i = 0; i < keys.length; i++) {\n key = keys[i];\n toVal = to[key];\n fromVal = from[key];\n if (!hasOwn(to, key)) {\n set(to, key, fromVal);\n } else if (isPlainObject(toVal) && isPlainObject(fromVal)) {\n mergeData(toVal, fromVal);\n }\n }\n return to\n}\n\n/**\n * Data\n */\nfunction mergeDataOrFn (\n parentVal,\n childVal,\n vm\n) {\n if (!vm) {\n // in a Vue.extend merge, both should be functions\n if (!childVal) {\n return parentVal\n }\n if (!parentVal) {\n return childVal\n }\n // when parentVal & childVal are both present,\n // we need to return a function that returns the\n // merged result of both functions... no need to\n // check if parentVal is a function here because\n // it has to be a function to pass previous merges.\n return function mergedDataFn () {\n return mergeData(\n typeof childVal === 'function' ? childVal.call(this, this) : childVal,\n typeof parentVal === 'function' ? parentVal.call(this, this) : parentVal\n )\n }\n } else {\n return function mergedInstanceDataFn () {\n // instance merge\n var instanceData = typeof childVal === 'function'\n ? childVal.call(vm, vm)\n : childVal;\n var defaultData = typeof parentVal === 'function'\n ? parentVal.call(vm, vm)\n : parentVal;\n if (instanceData) {\n return mergeData(instanceData, defaultData)\n } else {\n return defaultData\n }\n }\n }\n}\n\nstrats.data = function (\n parentVal,\n childVal,\n vm\n) {\n if (!vm) {\n if (childVal && typeof childVal !== 'function') {\n process.env.NODE_ENV !== 'production' && warn(\n 'The \"data\" option should be a function ' +\n 'that returns a per-instance value in component ' +\n 'definitions.',\n vm\n );\n\n return parentVal\n }\n return mergeDataOrFn(parentVal, childVal)\n }\n\n return mergeDataOrFn(parentVal, childVal, vm)\n};\n\n/**\n * Hooks and props are merged as arrays.\n */\nfunction mergeHook (\n parentVal,\n childVal\n) {\n return childVal\n ? parentVal\n ? parentVal.concat(childVal)\n : Array.isArray(childVal)\n ? childVal\n : [childVal]\n : parentVal\n}\n\nLIFECYCLE_HOOKS.forEach(function (hook) {\n strats[hook] = mergeHook;\n});\n\n/**\n * Assets\n *\n * When a vm is present (instance creation), we need to do\n * a three-way merge between constructor options, instance\n * options and parent options.\n */\nfunction mergeAssets (\n parentVal,\n childVal,\n vm,\n key\n) {\n var res = Object.create(parentVal || null);\n if (childVal) {\n process.env.NODE_ENV !== 'production' && assertObjectType(key, childVal, vm);\n return extend(res, childVal)\n } else {\n return res\n }\n}\n\nASSET_TYPES.forEach(function (type) {\n strats[type + 's'] = mergeAssets;\n});\n\n/**\n * Watchers.\n *\n * Watchers hashes should not overwrite one\n * another, so we merge them as arrays.\n */\nstrats.watch = function (\n parentVal,\n childVal,\n vm,\n key\n) {\n // work around Firefox's Object.prototype.watch...\n if (parentVal === nativeWatch) { parentVal = undefined; }\n if (childVal === nativeWatch) { childVal = undefined; }\n /* istanbul ignore if */\n if (!childVal) { return Object.create(parentVal || null) }\n if (process.env.NODE_ENV !== 'production') {\n assertObjectType(key, childVal, vm);\n }\n if (!parentVal) { return childVal }\n var ret = {};\n extend(ret, parentVal);\n for (var key$1 in childVal) {\n var parent = ret[key$1];\n var child = childVal[key$1];\n if (parent && !Array.isArray(parent)) {\n parent = [parent];\n }\n ret[key$1] = parent\n ? parent.concat(child)\n : Array.isArray(child) ? child : [child];\n }\n return ret\n};\n\n/**\n * Other object hashes.\n */\nstrats.props =\nstrats.methods =\nstrats.inject =\nstrats.computed = function (\n parentVal,\n childVal,\n vm,\n key\n) {\n if (childVal && process.env.NODE_ENV !== 'production') {\n assertObjectType(key, childVal, vm);\n }\n if (!parentVal) { return childVal }\n var ret = Object.create(null);\n extend(ret, parentVal);\n if (childVal) { extend(ret, childVal); }\n return ret\n};\nstrats.provide = mergeDataOrFn;\n\n/**\n * Default strategy.\n */\nvar defaultStrat = function (parentVal, childVal) {\n return childVal === undefined\n ? parentVal\n : childVal\n};\n\n/**\n * Validate component names\n */\nfunction checkComponents (options) {\n for (var key in options.components) {\n validateComponentName(key);\n }\n}\n\nfunction validateComponentName (name) {\n if (!/^[a-zA-Z][\\w-]*$/.test(name)) {\n warn(\n 'Invalid component name: \"' + name + '\". Component names ' +\n 'can only contain alphanumeric characters and the hyphen, ' +\n 'and must start with a letter.'\n );\n }\n if (isBuiltInTag(name) || config.isReservedTag(name)) {\n warn(\n 'Do not use built-in or reserved HTML elements as component ' +\n 'id: ' + name\n );\n }\n}\n\n/**\n * Ensure all props option syntax are normalized into the\n * Object-based format.\n */\nfunction normalizeProps (options, vm) {\n var props = options.props;\n if (!props) { return }\n var res = {};\n var i, val, name;\n if (Array.isArray(props)) {\n i = props.length;\n while (i--) {\n val = props[i];\n if (typeof val === 'string') {\n name = camelize(val);\n res[name] = { type: null };\n } else if (process.env.NODE_ENV !== 'production') {\n warn('props must be strings when using array syntax.');\n }\n }\n } else if (isPlainObject(props)) {\n for (var key in props) {\n val = props[key];\n name = camelize(key);\n res[name] = isPlainObject(val)\n ? val\n : { type: val };\n }\n } else if (process.env.NODE_ENV !== 'production') {\n warn(\n \"Invalid value for option \\\"props\\\": expected an Array or an Object, \" +\n \"but got \" + (toRawType(props)) + \".\",\n vm\n );\n }\n options.props = res;\n}\n\n/**\n * Normalize all injections into Object-based format\n */\nfunction normalizeInject (options, vm) {\n var inject = options.inject;\n if (!inject) { return }\n var normalized = options.inject = {};\n if (Array.isArray(inject)) {\n for (var i = 0; i < inject.length; i++) {\n normalized[inject[i]] = { from: inject[i] };\n }\n } else if (isPlainObject(inject)) {\n for (var key in inject) {\n var val = inject[key];\n normalized[key] = isPlainObject(val)\n ? extend({ from: key }, val)\n : { from: val };\n }\n } else if (process.env.NODE_ENV !== 'production') {\n warn(\n \"Invalid value for option \\\"inject\\\": expected an Array or an Object, \" +\n \"but got \" + (toRawType(inject)) + \".\",\n vm\n );\n }\n}\n\n/**\n * Normalize raw function directives into object format.\n */\nfunction normalizeDirectives (options) {\n var dirs = options.directives;\n if (dirs) {\n for (var key in dirs) {\n var def = dirs[key];\n if (typeof def === 'function') {\n dirs[key] = { bind: def, update: def };\n }\n }\n }\n}\n\nfunction assertObjectType (name, value, vm) {\n if (!isPlainObject(value)) {\n warn(\n \"Invalid value for option \\\"\" + name + \"\\\": expected an Object, \" +\n \"but got \" + (toRawType(value)) + \".\",\n vm\n );\n }\n}\n\n/**\n * Merge two option objects into a new one.\n * Core utility used in both instantiation and inheritance.\n */\nfunction mergeOptions (\n parent,\n child,\n vm\n) {\n if (process.env.NODE_ENV !== 'production') {\n checkComponents(child);\n }\n\n if (typeof child === 'function') {\n child = child.options;\n }\n\n normalizeProps(child, vm);\n normalizeInject(child, vm);\n normalizeDirectives(child);\n var extendsFrom = child.extends;\n if (extendsFrom) {\n parent = mergeOptions(parent, extendsFrom, vm);\n }\n if (child.mixins) {\n for (var i = 0, l = child.mixins.length; i < l; i++) {\n parent = mergeOptions(parent, child.mixins[i], vm);\n }\n }\n var options = {};\n var key;\n for (key in parent) {\n mergeField(key);\n }\n for (key in child) {\n if (!hasOwn(parent, key)) {\n mergeField(key);\n }\n }\n function mergeField (key) {\n var strat = strats[key] || defaultStrat;\n options[key] = strat(parent[key], child[key], vm, key);\n }\n return options\n}\n\n/**\n * Resolve an asset.\n * This function is used because child instances need access\n * to assets defined in its ancestor chain.\n */\nfunction resolveAsset (\n options,\n type,\n id,\n warnMissing\n) {\n /* istanbul ignore if */\n if (typeof id !== 'string') {\n return\n }\n var assets = options[type];\n // check local registration variations first\n if (hasOwn(assets, id)) { return assets[id] }\n var camelizedId = camelize(id);\n if (hasOwn(assets, camelizedId)) { return assets[camelizedId] }\n var PascalCaseId = capitalize(camelizedId);\n if (hasOwn(assets, PascalCaseId)) { return assets[PascalCaseId] }\n // fallback to prototype chain\n var res = assets[id] || assets[camelizedId] || assets[PascalCaseId];\n if (process.env.NODE_ENV !== 'production' && warnMissing && !res) {\n warn(\n 'Failed to resolve ' + type.slice(0, -1) + ': ' + id,\n options\n );\n }\n return res\n}\n\n/* */\n\nfunction validateProp (\n key,\n propOptions,\n propsData,\n vm\n) {\n var prop = propOptions[key];\n var absent = !hasOwn(propsData, key);\n var value = propsData[key];\n // boolean casting\n var booleanIndex = getTypeIndex(Boolean, prop.type);\n if (booleanIndex > -1) {\n if (absent && !hasOwn(prop, 'default')) {\n value = false;\n } else if (value === '' || value === hyphenate(key)) {\n // only cast empty string / same name to boolean if\n // boolean has higher priority\n var stringIndex = getTypeIndex(String, prop.type);\n if (stringIndex < 0 || booleanIndex < stringIndex) {\n value = true;\n }\n }\n }\n // check default value\n if (value === undefined) {\n value = getPropDefaultValue(vm, prop, key);\n // since the default value is a fresh copy,\n // make sure to observe it.\n var prevShouldObserve = shouldObserve;\n toggleObserving(true);\n observe(value);\n toggleObserving(prevShouldObserve);\n }\n if (\n process.env.NODE_ENV !== 'production' &&\n // skip validation for weex recycle-list child component props\n !(false && isObject(value) && ('@binding' in value))\n ) {\n assertProp(prop, key, value, vm, absent);\n }\n return value\n}\n\n/**\n * Get the default value of a prop.\n */\nfunction getPropDefaultValue (vm, prop, key) {\n // no default, return undefined\n if (!hasOwn(prop, 'default')) {\n return undefined\n }\n var def = prop.default;\n // warn against non-factory defaults for Object & Array\n if (process.env.NODE_ENV !== 'production' && isObject(def)) {\n warn(\n 'Invalid default value for prop \"' + key + '\": ' +\n 'Props with type Object/Array must use a factory function ' +\n 'to return the default value.',\n vm\n );\n }\n // the raw prop value was also undefined from previous render,\n // return previous default value to avoid unnecessary watcher trigger\n if (vm && vm.$options.propsData &&\n vm.$options.propsData[key] === undefined &&\n vm._props[key] !== undefined\n ) {\n return vm._props[key]\n }\n // call factory function for non-Function types\n // a value is Function if its prototype is function even across different execution context\n return typeof def === 'function' && getType(prop.type) !== 'Function'\n ? def.call(vm)\n : def\n}\n\n/**\n * Assert whether a prop is valid.\n */\nfunction assertProp (\n prop,\n name,\n value,\n vm,\n absent\n) {\n if (prop.required && absent) {\n warn(\n 'Missing required prop: \"' + name + '\"',\n vm\n );\n return\n }\n if (value == null && !prop.required) {\n return\n }\n var type = prop.type;\n var valid = !type || type === true;\n var expectedTypes = [];\n if (type) {\n if (!Array.isArray(type)) {\n type = [type];\n }\n for (var i = 0; i < type.length && !valid; i++) {\n var assertedType = assertType(value, type[i]);\n expectedTypes.push(assertedType.expectedType || '');\n valid = assertedType.valid;\n }\n }\n if (!valid) {\n warn(\n \"Invalid prop: type check failed for prop \\\"\" + name + \"\\\".\" +\n \" Expected \" + (expectedTypes.map(capitalize).join(', ')) +\n \", got \" + (toRawType(value)) + \".\",\n vm\n );\n return\n }\n var validator = prop.validator;\n if (validator) {\n if (!validator(value)) {\n warn(\n 'Invalid prop: custom validator check failed for prop \"' + name + '\".',\n vm\n );\n }\n }\n}\n\nvar simpleCheckRE = /^(String|Number|Boolean|Function|Symbol)$/;\n\nfunction assertType (value, type) {\n var valid;\n var expectedType = getType(type);\n if (simpleCheckRE.test(expectedType)) {\n var t = typeof value;\n valid = t === expectedType.toLowerCase();\n // for primitive wrapper objects\n if (!valid && t === 'object') {\n valid = value instanceof type;\n }\n } else if (expectedType === 'Object') {\n valid = isPlainObject(value);\n } else if (expectedType === 'Array') {\n valid = Array.isArray(value);\n } else {\n valid = value instanceof type;\n }\n return {\n valid: valid,\n expectedType: expectedType\n }\n}\n\n/**\n * Use function string name to check built-in types,\n * because a simple equality check will fail when running\n * across different vms / iframes.\n */\nfunction getType (fn) {\n var match = fn && fn.toString().match(/^\\s*function (\\w+)/);\n return match ? match[1] : ''\n}\n\nfunction isSameType (a, b) {\n return getType(a) === getType(b)\n}\n\nfunction getTypeIndex (type, expectedTypes) {\n if (!Array.isArray(expectedTypes)) {\n return isSameType(expectedTypes, type) ? 0 : -1\n }\n for (var i = 0, len = expectedTypes.length; i < len; i++) {\n if (isSameType(expectedTypes[i], type)) {\n return i\n }\n }\n return -1\n}\n\n/* */\n\nfunction handleError (err, vm, info) {\n if (vm) {\n var cur = vm;\n while ((cur = cur.$parent)) {\n var hooks = cur.$options.errorCaptured;\n if (hooks) {\n for (var i = 0; i < hooks.length; i++) {\n try {\n var capture = hooks[i].call(cur, err, vm, info) === false;\n if (capture) { return }\n } catch (e) {\n globalHandleError(e, cur, 'errorCaptured hook');\n }\n }\n }\n }\n }\n globalHandleError(err, vm, info);\n}\n\nfunction globalHandleError (err, vm, info) {\n if (config.errorHandler) {\n try {\n return config.errorHandler.call(null, err, vm, info)\n } catch (e) {\n logError(e, null, 'config.errorHandler');\n }\n }\n logError(err, vm, info);\n}\n\nfunction logError (err, vm, info) {\n if (process.env.NODE_ENV !== 'production') {\n warn((\"Error in \" + info + \": \\\"\" + (err.toString()) + \"\\\"\"), vm);\n }\n /* istanbul ignore else */\n if ((inBrowser || inWeex) && typeof console !== 'undefined') {\n console.error(err);\n } else {\n throw err\n }\n}\n\n/* */\n/* globals MessageChannel */\n\nvar callbacks = [];\nvar pending = false;\n\nfunction flushCallbacks () {\n pending = false;\n var copies = callbacks.slice(0);\n callbacks.length = 0;\n for (var i = 0; i < copies.length; i++) {\n copies[i]();\n }\n}\n\n// Here we have async deferring wrappers using both microtasks and (macro) tasks.\n// In < 2.4 we used microtasks everywhere, but there are some scenarios where\n// microtasks have too high a priority and fire in between supposedly\n// sequential events (e.g. #4521, #6690) or even between bubbling of the same\n// event (#6566). However, using (macro) tasks everywhere also has subtle problems\n// when state is changed right before repaint (e.g. #6813, out-in transitions).\n// Here we use microtask by default, but expose a way to force (macro) task when\n// needed (e.g. in event handlers attached by v-on).\nvar microTimerFunc;\nvar macroTimerFunc;\nvar useMacroTask = false;\n\n// Determine (macro) task defer implementation.\n// Technically setImmediate should be the ideal choice, but it's only available\n// in IE. The only polyfill that consistently queues the callback after all DOM\n// events triggered in the same loop is by using MessageChannel.\n/* istanbul ignore if */\nif (typeof setImmediate !== 'undefined' && isNative(setImmediate)) {\n macroTimerFunc = function () {\n setImmediate(flushCallbacks);\n };\n} else if (typeof MessageChannel !== 'undefined' && (\n isNative(MessageChannel) ||\n // PhantomJS\n MessageChannel.toString() === '[object MessageChannelConstructor]'\n)) {\n var channel = new MessageChannel();\n var port = channel.port2;\n channel.port1.onmessage = flushCallbacks;\n macroTimerFunc = function () {\n port.postMessage(1);\n };\n} else {\n /* istanbul ignore next */\n macroTimerFunc = function () {\n setTimeout(flushCallbacks, 0);\n };\n}\n\n// Determine microtask defer implementation.\n/* istanbul ignore next, $flow-disable-line */\nif (typeof Promise !== 'undefined' && isNative(Promise)) {\n var p = Promise.resolve();\n microTimerFunc = function () {\n p.then(flushCallbacks);\n // in problematic UIWebViews, Promise.then doesn't completely break, but\n // it can get stuck in a weird state where callbacks are pushed into the\n // microtask queue but the queue isn't being flushed, until the browser\n // needs to do some other work, e.g. handle a timer. Therefore we can\n // \"force\" the microtask queue to be flushed by adding an empty timer.\n if (isIOS) { setTimeout(noop); }\n };\n} else {\n // fallback to macro\n microTimerFunc = macroTimerFunc;\n}\n\n/**\n * Wrap a function so that if any code inside triggers state change,\n * the changes are queued using a (macro) task instead of a microtask.\n */\nfunction withMacroTask (fn) {\n return fn._withTask || (fn._withTask = function () {\n useMacroTask = true;\n var res = fn.apply(null, arguments);\n useMacroTask = false;\n return res\n })\n}\n\nfunction nextTick (cb, ctx) {\n var _resolve;\n callbacks.push(function () {\n if (cb) {\n try {\n cb.call(ctx);\n } catch (e) {\n handleError(e, ctx, 'nextTick');\n }\n } else if (_resolve) {\n _resolve(ctx);\n }\n });\n if (!pending) {\n pending = true;\n if (useMacroTask) {\n macroTimerFunc();\n } else {\n microTimerFunc();\n }\n }\n // $flow-disable-line\n if (!cb && typeof Promise !== 'undefined') {\n return new Promise(function (resolve) {\n _resolve = resolve;\n })\n }\n}\n\n/* */\n\n/* not type checking this file because flow doesn't play well with Proxy */\n\nvar initProxy;\n\nif (process.env.NODE_ENV !== 'production') {\n var allowedGlobals = makeMap(\n 'Infinity,undefined,NaN,isFinite,isNaN,' +\n 'parseFloat,parseInt,decodeURI,decodeURIComponent,encodeURI,encodeURIComponent,' +\n 'Math,Number,Date,Array,Object,Boolean,String,RegExp,Map,Set,JSON,Intl,' +\n 'require' // for Webpack/Browserify\n );\n\n var warnNonPresent = function (target, key) {\n warn(\n \"Property or method \\\"\" + key + \"\\\" is not defined on the instance but \" +\n 'referenced during render. Make sure that this property is reactive, ' +\n 'either in the data option, or for class-based components, by ' +\n 'initializing the property. ' +\n 'See: https://vuejs.org/v2/guide/reactivity.html#Declaring-Reactive-Properties.',\n target\n );\n };\n\n var hasProxy =\n typeof Proxy !== 'undefined' && isNative(Proxy);\n\n if (hasProxy) {\n var isBuiltInModifier = makeMap('stop,prevent,self,ctrl,shift,alt,meta,exact');\n config.keyCodes = new Proxy(config.keyCodes, {\n set: function set (target, key, value) {\n if (isBuiltInModifier(key)) {\n warn((\"Avoid overwriting built-in modifier in config.keyCodes: .\" + key));\n return false\n } else {\n target[key] = value;\n return true\n }\n }\n });\n }\n\n var hasHandler = {\n has: function has (target, key) {\n var has = key in target;\n var isAllowed = allowedGlobals(key) || key.charAt(0) === '_';\n if (!has && !isAllowed) {\n warnNonPresent(target, key);\n }\n return has || !isAllowed\n }\n };\n\n var getHandler = {\n get: function get (target, key) {\n if (typeof key === 'string' && !(key in target)) {\n warnNonPresent(target, key);\n }\n return target[key]\n }\n };\n\n initProxy = function initProxy (vm) {\n if (hasProxy) {\n // determine which proxy handler to use\n var options = vm.$options;\n var handlers = options.render && options.render._withStripped\n ? getHandler\n : hasHandler;\n vm._renderProxy = new Proxy(vm, handlers);\n } else {\n vm._renderProxy = vm;\n }\n };\n}\n\n/* */\n\nvar seenObjects = new _Set();\n\n/**\n * Recursively traverse an object to evoke all converted\n * getters, so that every nested property inside the object\n * is collected as a \"deep\" dependency.\n */\nfunction traverse (val) {\n _traverse(val, seenObjects);\n seenObjects.clear();\n}\n\nfunction _traverse (val, seen) {\n var i, keys;\n var isA = Array.isArray(val);\n if ((!isA && !isObject(val)) || Object.isFrozen(val) || val instanceof VNode) {\n return\n }\n if (val.__ob__) {\n var depId = val.__ob__.dep.id;\n if (seen.has(depId)) {\n return\n }\n seen.add(depId);\n }\n if (isA) {\n i = val.length;\n while (i--) { _traverse(val[i], seen); }\n } else {\n keys = Object.keys(val);\n i = keys.length;\n while (i--) { _traverse(val[keys[i]], seen); }\n }\n}\n\nvar mark;\nvar measure;\n\nif (process.env.NODE_ENV !== 'production') {\n var perf = inBrowser && window.performance;\n /* istanbul ignore if */\n if (\n perf &&\n perf.mark &&\n perf.measure &&\n perf.clearMarks &&\n perf.clearMeasures\n ) {\n mark = function (tag) { return perf.mark(tag); };\n measure = function (name, startTag, endTag) {\n perf.measure(name, startTag, endTag);\n perf.clearMarks(startTag);\n perf.clearMarks(endTag);\n perf.clearMeasures(name);\n };\n }\n}\n\n/* */\n\nvar normalizeEvent = cached(function (name) {\n var passive = name.charAt(0) === '&';\n name = passive ? name.slice(1) : name;\n var once$$1 = name.charAt(0) === '~'; // Prefixed last, checked first\n name = once$$1 ? name.slice(1) : name;\n var capture = name.charAt(0) === '!';\n name = capture ? name.slice(1) : name;\n return {\n name: name,\n once: once$$1,\n capture: capture,\n passive: passive\n }\n});\n\nfunction createFnInvoker (fns) {\n function invoker () {\n var arguments$1 = arguments;\n\n var fns = invoker.fns;\n if (Array.isArray(fns)) {\n var cloned = fns.slice();\n for (var i = 0; i < cloned.length; i++) {\n cloned[i].apply(null, arguments$1);\n }\n } else {\n // return handler return value for single handlers\n return fns.apply(null, arguments)\n }\n }\n invoker.fns = fns;\n return invoker\n}\n\nfunction updateListeners (\n on,\n oldOn,\n add,\n remove$$1,\n vm\n) {\n var name, def, cur, old, event;\n for (name in on) {\n def = cur = on[name];\n old = oldOn[name];\n event = normalizeEvent(name);\n /* istanbul ignore if */\n if (isUndef(cur)) {\n process.env.NODE_ENV !== 'production' && warn(\n \"Invalid handler for event \\\"\" + (event.name) + \"\\\": got \" + String(cur),\n vm\n );\n } else if (isUndef(old)) {\n if (isUndef(cur.fns)) {\n cur = on[name] = createFnInvoker(cur);\n }\n add(event.name, cur, event.once, event.capture, event.passive, event.params);\n } else if (cur !== old) {\n old.fns = cur;\n on[name] = old;\n }\n }\n for (name in oldOn) {\n if (isUndef(on[name])) {\n event = normalizeEvent(name);\n remove$$1(event.name, oldOn[name], event.capture);\n }\n }\n}\n\n/* */\n\nfunction mergeVNodeHook (def, hookKey, hook) {\n if (def instanceof VNode) {\n def = def.data.hook || (def.data.hook = {});\n }\n var invoker;\n var oldHook = def[hookKey];\n\n function wrappedHook () {\n hook.apply(this, arguments);\n // important: remove merged hook to ensure it's called only once\n // and prevent memory leak\n remove(invoker.fns, wrappedHook);\n }\n\n if (isUndef(oldHook)) {\n // no existing hook\n invoker = createFnInvoker([wrappedHook]);\n } else {\n /* istanbul ignore if */\n if (isDef(oldHook.fns) && isTrue(oldHook.merged)) {\n // already a merged invoker\n invoker = oldHook;\n invoker.fns.push(wrappedHook);\n } else {\n // existing plain hook\n invoker = createFnInvoker([oldHook, wrappedHook]);\n }\n }\n\n invoker.merged = true;\n def[hookKey] = invoker;\n}\n\n/* */\n\nfunction extractPropsFromVNodeData (\n data,\n Ctor,\n tag\n) {\n // we are only extracting raw values here.\n // validation and default values are handled in the child\n // component itself.\n var propOptions = Ctor.options.props;\n if (isUndef(propOptions)) {\n return\n }\n var res = {};\n var attrs = data.attrs;\n var props = data.props;\n if (isDef(attrs) || isDef(props)) {\n for (var key in propOptions) {\n var altKey = hyphenate(key);\n if (process.env.NODE_ENV !== 'production') {\n var keyInLowerCase = key.toLowerCase();\n if (\n key !== keyInLowerCase &&\n attrs && hasOwn(attrs, keyInLowerCase)\n ) {\n tip(\n \"Prop \\\"\" + keyInLowerCase + \"\\\" is passed to component \" +\n (formatComponentName(tag || Ctor)) + \", but the declared prop name is\" +\n \" \\\"\" + key + \"\\\". \" +\n \"Note that HTML attributes are case-insensitive and camelCased \" +\n \"props need to use their kebab-case equivalents when using in-DOM \" +\n \"templates. You should probably use \\\"\" + altKey + \"\\\" instead of \\\"\" + key + \"\\\".\"\n );\n }\n }\n checkProp(res, props, key, altKey, true) ||\n checkProp(res, attrs, key, altKey, false);\n }\n }\n return res\n}\n\nfunction checkProp (\n res,\n hash,\n key,\n altKey,\n preserve\n) {\n if (isDef(hash)) {\n if (hasOwn(hash, key)) {\n res[key] = hash[key];\n if (!preserve) {\n delete hash[key];\n }\n return true\n } else if (hasOwn(hash, altKey)) {\n res[key] = hash[altKey];\n if (!preserve) {\n delete hash[altKey];\n }\n return true\n }\n }\n return false\n}\n\n/* */\n\n// The template compiler attempts to minimize the need for normalization by\n// statically analyzing the template at compile time.\n//\n// For plain HTML markup, normalization can be completely skipped because the\n// generated render function is guaranteed to return Array. There are\n// two cases where extra normalization is needed:\n\n// 1. When the children contains components - because a functional component\n// may return an Array instead of a single root. In this case, just a simple\n// normalization is needed - if any child is an Array, we flatten the whole\n// thing with Array.prototype.concat. It is guaranteed to be only 1-level deep\n// because functional components already normalize their own children.\nfunction simpleNormalizeChildren (children) {\n for (var i = 0; i < children.length; i++) {\n if (Array.isArray(children[i])) {\n return Array.prototype.concat.apply([], children)\n }\n }\n return children\n}\n\n// 2. When the children contains constructs that always generated nested Arrays,\n// e.g.