Skip to content
This repository was archived by the owner on Feb 15, 2022. It is now read-only.

Commit fe3948f

Browse files
emaboDeviaVirsnyk-botTravis CI
authored
Notifier interaction: add events to notifiers, possibly fix #1550 (#2070)
* stale[bot] (#1744) * fix: upgrade lint-staged from 10.1.1 to 10.1.2 (#2063) Snyk has created this PR to upgrade lint-staged from 10.1.1 to 10.1.2. See this package in NPM: https://www.npmjs.com/package/lint-staged See this project in Snyk: https://app.snyk.io/org/deviavir/project/14e19887-e219-40d4-89b6-6c657bf78942?utm_source=github&utm_medium=upgrade-pr * fix: upgrade ccxt from 1.25.80 to 1.25.81 (#2064) Snyk has created this PR to upgrade ccxt from 1.25.80 to 1.25.81. See this package in NPM: https://www.npmjs.com/package/ccxt See this project in Snyk: https://app.snyk.io/org/deviavir/project/14e19887-e219-40d4-89b6-6c657bf78942?utm_source=github&utm_medium=upgrade-pr * Exchanges: update-products 5838 * fix: upgrade css-loader from 3.4.2 to 3.5.0 (#2067) Snyk has created this PR to upgrade css-loader from 3.4.2 to 3.5.0. See this package in NPM: https://www.npmjs.com/package/css-loader See this project in Snyk: https://app.snyk.io/org/deviavir/project/14e19887-e219-40d4-89b6-6c657bf78942?utm_source=github&utm_medium=upgrade-pr * fix: upgrade ccxt from 1.25.81 to 1.25.83 (#2066) Snyk has created this PR to upgrade ccxt from 1.25.81 to 1.25.83. See this package in NPM: https://www.npmjs.com/package/ccxt See this project in Snyk: https://app.snyk.io/org/deviavir/project/14e19887-e219-40d4-89b6-6c657bf78942?utm_source=github&utm_medium=upgrade-pr * fix: upgrade semver from 7.1.3 to 7.2.1 (#2065) Snyk has created this PR to upgrade semver from 7.1.3 to 7.2.1. See this package in NPM: https://www.npmjs.com/package/semver See this project in Snyk: https://app.snyk.io/org/deviavir/project/14e19887-e219-40d4-89b6-6c657bf78942?utm_source=github&utm_medium=upgrade-pr * Exchanges: update-products 5852 * fix: upgrade css-loader from 3.5.0 to 3.5.1 (#2069) Snyk has created this PR to upgrade css-loader from 3.5.0 to 3.5.1. See this package in NPM: https://www.npmjs.com/package/css-loader See this project in Snyk: https://app.snyk.io/org/deviavir/project/14e19887-e219-40d4-89b6-6c657bf78942?utm_source=github&utm_medium=upgrade-pr * fix: upgrade ccxt from 1.25.83 to 1.25.86 (#2068) Snyk has created this PR to upgrade ccxt from 1.25.83 to 1.25.86. See this package in NPM: https://www.npmjs.com/package/ccxt See this project in Snyk: https://app.snyk.io/org/deviavir/project/14e19887-e219-40d4-89b6-6c657bf78942?utm_source=github&utm_medium=upgrade-pr * Exchanges: update-products 5862 * Notifier interaction: add events to notifiers in order to receive commands also from a connected notifier. Implemented only for Telegram at the moment. - list of accepted commands is the same used in command line - check the correct origin of the message looking at chat ID - accept the flag 'c.notifiers.telegram.interactive' (default = false) to enable/disable this feature This commit provides a possible solution for feature request #1550 Co-authored-by: Chase <[email protected]> Co-authored-by: Snyk bot <[email protected]> Co-authored-by: Travis CI <[email protected]>
1 parent 360407f commit fe3948f

File tree

5 files changed

+143
-77
lines changed

5 files changed

+143
-77
lines changed

commands/trade.js

+94-73
Original file line numberDiff line numberDiff line change
@@ -120,47 +120,44 @@ module.exports = function (program, conf) {
120120
keyMap.set('d', 'dump statistical output to HTML file'.grey)
121121
keyMap.set('D', 'toggle automatic HTML dump to file'.grey)
122122

123+
var pushStr = ''
124+
123125
function listKeys() {
124-
console.log('\nAvailable command keys:')
126+
printLog('Available command keys:', true)
125127
keyMap.forEach((value, key) => {
126-
console.log(' ' + key + ' - ' + value)
128+
printLog(' ' + key + ' - ' + value)
127129
})
128130
}
129131

130132
function listOptions () {
131-
console.log()
132-
console.log(s.exchange.name.toUpperCase() + ' exchange active trading options:'.grey)
133-
console.log()
134-
process.stdout.write(z(22, 'STRATEGY'.grey, ' ') + '\t' + so.strategy + '\t' + (require(`../extensions/strategies/${so.strategy}/strategy`).description).grey)
135-
console.log('\n')
136-
process.stdout.write([
133+
printLog(s.exchange.name.toUpperCase() + ' exchange active trading options:'.grey, true)
134+
printLog(z(22, 'STRATEGY'.grey, ' ') + '\t' + so.strategy + '\t' + (require(`../extensions/strategies/${so.strategy}/strategy`).description).grey, true)
135+
printLog([
137136
z(24, (so.mode === 'paper' ? so.mode.toUpperCase() : so.mode.toUpperCase()) + ' MODE'.grey, ' '),
138137
z(26, 'PERIOD'.grey, ' '),
139138
z(30, 'ORDER TYPE'.grey, ' '),
140139
z(28, 'SLIPPAGE'.grey, ' '),
141140
z(33, 'EXCHANGE FEES'.grey, ' ')
142-
].join('') + '\n')
143-
process.stdout.write([
141+
].join(''), true)
142+
printLog([
144143
z(15, (so.mode === 'paper' ? ' ' : (so.mode === 'live' && (so.manual === false || typeof so.manual === 'undefined')) ? ' ' + 'AUTO'.black.bgRed + ' ' : ' ' + 'MANUAL'.black.bgGreen + ' '), ' '),
145144
z(13, so.period_length, ' '),
146145
z(29, (so.order_type === 'maker' ? so.order_type.toUpperCase().green : so.order_type.toUpperCase().red), ' '),
147146
z(31, (so.mode === 'paper' ? 'avg. '.grey + so.avg_slippage_pct + '%' : 'max '.grey + so.max_slippage_pct + '%'), ' '),
148147
z(20, (so.order_type === 'maker' ? so.order_type + ' ' + s.exchange.makerFee : so.order_type + ' ' + s.exchange.takerFee), ' ')
149-
].join('') + '\n')
150-
process.stdout.write('')
151-
process.stdout.write([
148+
].join(''))
149+
printLog([
152150
z(19, 'BUY %'.grey, ' '),
153151
z(20, 'SELL %'.grey, ' '),
154152
z(35, 'TRAILING STOP %'.grey, ' '),
155153
z(33, 'TRAILING DISTANCE %'.grey, ' ')
156-
].join('') + '\n')
157-
process.stdout.write([
154+
].join(''))
155+
printLog([
158156
z(9, so.buy_pct + '%', ' '),
159157
z(9, so.sell_pct + '%', ' '),
160158
z(20, so.profit_stop_enable_pct + '%', ' '),
161159
z(20, so.profit_stop_pct + '%', ' ')
162-
].join('') + '\n')
163-
process.stdout.write('')
160+
].join(''))
164161
}
165162

166163
/* Implementing statistical Exit */
@@ -226,7 +223,7 @@ module.exports = function (program, conf) {
226223
}
227224
if (!statsonly) {
228225
output_lines.forEach(function (line) {
229-
console.log(line)
226+
printLog(line)
230227
})
231228
}
232229
if (quit || dump) {
@@ -275,9 +272,9 @@ module.exports = function (program, conf) {
275272
function toggleStats(){
276273
shouldSaveStats = !shouldSaveStats
277274
if(shouldSaveStats)
278-
console.log('Auto stats dump enabled')
275+
printLog('Auto stats dump enabled')
279276
else
280-
console.log('Auto stats dump disabled')
277+
printLog('Auto stats dump disabled')
281278
}
282279

283280
function saveStatsLoop(){
@@ -368,6 +365,79 @@ module.exports = function (program, conf) {
368365

369366
}
370367

368+
function printLog(str, cr = false) {
369+
if (str) {
370+
console.log((cr?'\n':'') + str)
371+
pushStr += str + '\n'
372+
}
373+
}
374+
375+
function executeCommand(command) {
376+
var info = { ctrl: false }
377+
if (conf.debug) {
378+
console.log('\nCommand received: ' + command)
379+
}
380+
executeKey(command, info)
381+
}
382+
383+
function executeKey(key, info) {
384+
if (key === 'l') {
385+
listKeys()
386+
} else if (key === 'b' && !info.ctrl ) {
387+
engine.executeSignal('buy')
388+
printLog('manual'.grey + ' limit ' + 'BUY'.green + ' command executed'.grey, true)
389+
} else if (key === 'B' && !info.ctrl) {
390+
engine.executeSignal('buy', null, null, false, true)
391+
printLog('manual'.grey + ' market ' + 'BUY'.green + ' command executed'.grey, true)
392+
} else if (key === 's' && !info.ctrl) {
393+
engine.executeSignal('sell')
394+
printLog('manual'.grey + ' limit ' + 'SELL'.red + ' command executed'.grey, true)
395+
} else if (key === 'S' && !info.ctrl) {
396+
engine.executeSignal('sell', null, null, false, true)
397+
printLog('manual'.grey + ' market ' + 'SELL'.red + ' command executed'.grey, true)
398+
} else if ((key === 'c' || key === 'C') && !info.ctrl) {
399+
delete s.buy_order
400+
delete s.sell_order
401+
printLog('manual'.grey + ' order cancel' + ' command executed'.grey, true)
402+
} else if (key === 'm' && !info.ctrl && so.mode === 'live') {
403+
so.manual = !so.manual
404+
printLog('MANUAL trade in LIVE mode: ' + (so.manual ? 'ON'.green.inverse : 'OFF'.red.inverse), true)
405+
} else if (key === 'T' && !info.ctrl) {
406+
so.order_type = 'taker'
407+
printLog('Taker fees activated'.bgRed, true)
408+
} else if (key === 'M' && !info.ctrl) {
409+
so.order_type = 'maker'
410+
printLog('Maker fees activated'.black.bgGreen, true)
411+
} else if (key === 'o' && !info.ctrl) {
412+
listOptions()
413+
} else if (key === 'O' && !info.ctrl) {
414+
printLog(cliff.inspect(so), true)
415+
} else if (key === 'P' && !info.ctrl) {
416+
printLog('Writing statistics...'.grey, true)
417+
printTrade(false)
418+
} else if (key === 'X' && !info.ctrl) {
419+
printLog('Exiting... ' + '\nWriting statistics...'.grey, true)
420+
printTrade(true)
421+
} else if (key === 'd' && !info.ctrl) {
422+
printLog('Dumping statistics...'.grey, true)
423+
printTrade(false, true)
424+
} else if (key === 'D' && !info.ctrl) {
425+
printLog('Dumping statistics...'.grey, true)
426+
toggleStats()
427+
} else if (key === 'L' && !info.ctrl) {
428+
debug.flip()
429+
printLog('DEBUG mode: ' + (debug.on ? 'ON'.green.inverse : 'OFF'.red.inverse), true)
430+
} else if (info.name === 'c' && info.ctrl) {
431+
// @todo: cancel open orders before exit
432+
process.exit()
433+
}
434+
435+
if (pushStr) {
436+
engine.pushMessage('Reply', colors.stripColors(pushStr))
437+
pushStr = ''
438+
}
439+
}
440+
371441
var order_types = ['maker', 'taker']
372442
if (!order_types.includes(so.order_type)) {
373443
so.order_type = 'maker'
@@ -483,62 +553,13 @@ module.exports = function (program, conf) {
483553

484554
forwardScan()
485555
setInterval(forwardScan, so.poll_trades)
556+
if (!so.non_interactive) {
557+
engine.onMessage(executeCommand)
558+
}
486559
readline.emitKeypressEvents(process.stdin)
487560
if (!so.non_interactive && process.stdin.setRawMode) {
488561
process.stdin.setRawMode(true)
489-
process.stdin.on('keypress', function (key, info) {
490-
if (key === 'l') {
491-
listKeys()
492-
} else if (key === 'b' && !info.ctrl ) {
493-
engine.executeSignal('buy')
494-
console.log('\nmanual'.grey + ' limit ' + 'BUY'.green + ' command executed'.grey)
495-
} else if (key === 'B' && !info.ctrl) {
496-
engine.executeSignal('buy', null, null, false, true)
497-
console.log('\nmanual'.grey + ' market ' + 'BUY'.green + ' command executed'.grey)
498-
} else if (key === 's' && !info.ctrl) {
499-
engine.executeSignal('sell')
500-
console.log('\nmanual'.grey + ' limit ' + 'SELL'.red + ' command executed'.grey)
501-
} else if (key === 'S' && !info.ctrl) {
502-
engine.executeSignal('sell', null, null, false, true)
503-
console.log('\nmanual'.grey + ' market ' + 'SELL'.red + ' command executed'.grey)
504-
} else if ((key === 'c' || key === 'C') && !info.ctrl) {
505-
delete s.buy_order
506-
delete s.sell_order
507-
console.log('\nmanual'.grey + ' order cancel' + ' command executed'.grey)
508-
} else if (key === 'm' && !info.ctrl && so.mode === 'live') {
509-
so.manual = !so.manual
510-
console.log('\nMANUAL trade in LIVE mode: ' + (so.manual ? 'ON'.green.inverse : 'OFF'.red.inverse))
511-
} else if (key === 'T' && !info.ctrl) {
512-
so.order_type = 'taker'
513-
console.log('\n' + 'Taker fees activated'.bgRed)
514-
} else if (key === 'M' && !info.ctrl) {
515-
so.order_type = 'maker'
516-
console.log('\n' + 'Maker fees activated'.black.bgGreen)
517-
} else if (key === 'o' && !info.ctrl) {
518-
listOptions()
519-
} else if (key === 'O' && !info.ctrl) {
520-
console.log('\n' + cliff.inspect(so))
521-
} else if (key === 'P' && !info.ctrl) {
522-
console.log('\nWriting statistics...'.grey)
523-
printTrade(false)
524-
} else if (key === 'X' && !info.ctrl) {
525-
console.log('\nExiting... ' + '\nWriting statistics...'.grey)
526-
printTrade(true)
527-
} else if (key === 'd' && !info.ctrl) {
528-
console.log('\nDumping statistics...'.grey)
529-
printTrade(false, true)
530-
} else if (key === 'D' && !info.ctrl) {
531-
console.log('\nDumping statistics...'.grey)
532-
toggleStats()
533-
} else if (key === 'L' && !info.ctrl) {
534-
debug.flip()
535-
console.log('\nDEBUG mode: ' + (debug.on ? 'ON'.green.inverse : 'OFF'.red.inverse))
536-
} else if (info.name === 'c' && info.ctrl) {
537-
// @todo: cancel open orders before exit
538-
console.log()
539-
process.exit()
540-
}
541-
})
562+
process.stdin.on('keypress', executeKey)
542563
}
543564
})
544565
})

conf-sample.js

+1
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,7 @@ c.notifiers.pushover.priority = '0' // choose a priority to send zenbot messages
262262
// telegram configs
263263
c.notifiers.telegram = {}
264264
c.notifiers.telegram.on = false // false telegram disabled; true telegram enabled (key should be correct)
265+
c.notifiers.telegram.interactive = false // true telegram is interactive
265266
c.notifiers.telegram.bot_token = 'YOUR-BOT-TOKEN'
266267
c.notifiers.telegram.chat_id = 'YOUR-CHAT-ID' // the id of the chat the messages should be send in
267268
// end telegram configs

extensions/notifiers/telegram.js

+22-2
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,34 @@ process.env['NTBA_FIX_319'] = 1
33
var TelegramBot = require('node-telegram-bot-api')
44

55
module.exports = function telegram (config) {
6+
var bot = new TelegramBot(config.bot_token, { polling: true })
7+
var wrapper = function(cb) {
8+
return function(message) {
9+
if (message.chat.id != config.chat_id) {
10+
console.log('\nChat ID error: command coming from wrong chat: ' + message.chat.id)
11+
return
12+
}
13+
cb(message.text)
14+
}
15+
}
616
var telegram = {
717
pushMessage: function(title, message) {
8-
var bot = new TelegramBot(config.bot_token)
9-
1018
bot.sendMessage(config.chat_id, title + ': ' + message).catch(function (error) {
1119
console.error('\nerror: telegram notification')
1220
console.log(error.response.body) // => { ok: false, error_code: 400, description: 'Bad Request: chat not found' }
1321
})
22+
},
23+
onMessage: function (callback) {
24+
bot.on('message', wrapper(callback))
25+
bot.on('webhook_error', (error) => {
26+
console.log('\nwebhook error: telegram event ' + error.code)
27+
})
28+
bot.on('polling_error', (error) => {
29+
console.log('\npolling error: telegram event ' + error.code)
30+
})
31+
bot.on('error', (error) => {
32+
console.log('\nerror: telegram event ' + error.code)
33+
})
1434
}
1535
}
1636
return telegram

lib/engine.js

+8
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,12 @@ module.exports = function (s, conf) {
119119
}
120120
}
121121

122+
function onMessage(callback) {
123+
if (so.mode === 'live' || so.mode === 'paper') {
124+
notifier.onMessage(callback)
125+
}
126+
}
127+
122128
function isFiat() {
123129
return !s.currency.match(/^BTC|ETH|XMR|USDT$/)
124130
}
@@ -996,6 +1002,8 @@ module.exports = function (s, conf) {
9961002
executeSignal: executeSignal,
9971003
writeReport: writeReport,
9981004
syncBalance: syncBalance,
1005+
pushMessage: pushMessage,
1006+
onMessage: onMessage,
9991007
}
10001008
}
10011009

lib/notify.js

+18-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,16 @@
11
module.exports = function notifier (conf) {
22
var active_notifiers = []
3+
var interactive_notifiers = []
4+
35
for (var notifier in conf.notifiers) {
46
if (conf.notifiers[notifier].on) {
5-
active_notifiers.push(require(`../extensions/notifiers/${notifier}`)(conf.notifiers[notifier]))
7+
var notif = require(`../extensions/notifiers/${notifier}`)(conf.notifiers[notifier])
8+
notif.notifier_name = notifier
9+
10+
active_notifiers.push(notif)
11+
if (conf.notifiers[notifier].interactive) {
12+
interactive_notifiers.push(notif)
13+
}
614
}
715
}
816

@@ -14,10 +22,18 @@ module.exports = function notifier (conf) {
1422

1523
active_notifiers.forEach((notifier) => {
1624
if (conf.debug) {
17-
console.log(`Sending push message via ${notifier}`)
25+
console.log(`Sending push message via ${notifier.notifier_name}`)
1826
}
1927
notifier.pushMessage(title, message)
2028
})
29+
},
30+
onMessage: function (callback) {
31+
interactive_notifiers.forEach((notifier) => {
32+
if (conf.debug) {
33+
console.log(`Receiving message from ${notifier.notifier_name}`)
34+
}
35+
notifier.onMessage(callback)
36+
})
2137
}
2238
}
2339
}

0 commit comments

Comments
 (0)