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

Notifier interaction: add events to notifiers, possibly fix #1550 #2070

Merged
merged 15 commits into from
Apr 10, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
167 changes: 94 additions & 73 deletions commands/trade.js
Original file line number Diff line number Diff line change
Expand Up @@ -120,47 +120,44 @@ module.exports = function (program, conf) {
keyMap.set('d', 'dump statistical output to HTML file'.grey)
keyMap.set('D', 'toggle automatic HTML dump to file'.grey)

var pushStr = ''

function listKeys() {
console.log('\nAvailable command keys:')
printLog('Available command keys:', true)
keyMap.forEach((value, key) => {
console.log(' ' + key + ' - ' + value)
printLog(' ' + key + ' - ' + value)
})
}

function listOptions () {
console.log()
console.log(s.exchange.name.toUpperCase() + ' exchange active trading options:'.grey)
console.log()
process.stdout.write(z(22, 'STRATEGY'.grey, ' ') + '\t' + so.strategy + '\t' + (require(`../extensions/strategies/${so.strategy}/strategy`).description).grey)
console.log('\n')
process.stdout.write([
printLog(s.exchange.name.toUpperCase() + ' exchange active trading options:'.grey, true)
printLog(z(22, 'STRATEGY'.grey, ' ') + '\t' + so.strategy + '\t' + (require(`../extensions/strategies/${so.strategy}/strategy`).description).grey, true)
printLog([
z(24, (so.mode === 'paper' ? so.mode.toUpperCase() : so.mode.toUpperCase()) + ' MODE'.grey, ' '),
z(26, 'PERIOD'.grey, ' '),
z(30, 'ORDER TYPE'.grey, ' '),
z(28, 'SLIPPAGE'.grey, ' '),
z(33, 'EXCHANGE FEES'.grey, ' ')
].join('') + '\n')
process.stdout.write([
].join(''), true)
printLog([
z(15, (so.mode === 'paper' ? ' ' : (so.mode === 'live' && (so.manual === false || typeof so.manual === 'undefined')) ? ' ' + 'AUTO'.black.bgRed + ' ' : ' ' + 'MANUAL'.black.bgGreen + ' '), ' '),
z(13, so.period_length, ' '),
z(29, (so.order_type === 'maker' ? so.order_type.toUpperCase().green : so.order_type.toUpperCase().red), ' '),
z(31, (so.mode === 'paper' ? 'avg. '.grey + so.avg_slippage_pct + '%' : 'max '.grey + so.max_slippage_pct + '%'), ' '),
z(20, (so.order_type === 'maker' ? so.order_type + ' ' + s.exchange.makerFee : so.order_type + ' ' + s.exchange.takerFee), ' ')
].join('') + '\n')
process.stdout.write('')
process.stdout.write([
].join(''))
printLog([
z(19, 'BUY %'.grey, ' '),
z(20, 'SELL %'.grey, ' '),
z(35, 'TRAILING STOP %'.grey, ' '),
z(33, 'TRAILING DISTANCE %'.grey, ' ')
].join('') + '\n')
process.stdout.write([
].join(''))
printLog([
z(9, so.buy_pct + '%', ' '),
z(9, so.sell_pct + '%', ' '),
z(20, so.profit_stop_enable_pct + '%', ' '),
z(20, so.profit_stop_pct + '%', ' ')
].join('') + '\n')
process.stdout.write('')
].join(''))
}

/* Implementing statistical Exit */
Expand Down Expand Up @@ -226,7 +223,7 @@ module.exports = function (program, conf) {
}
if (!statsonly) {
output_lines.forEach(function (line) {
console.log(line)
printLog(line)
})
}
if (quit || dump) {
Expand Down Expand Up @@ -275,9 +272,9 @@ module.exports = function (program, conf) {
function toggleStats(){
shouldSaveStats = !shouldSaveStats
if(shouldSaveStats)
console.log('Auto stats dump enabled')
printLog('Auto stats dump enabled')
else
console.log('Auto stats dump disabled')
printLog('Auto stats dump disabled')
}

function saveStatsLoop(){
Expand Down Expand Up @@ -368,6 +365,79 @@ module.exports = function (program, conf) {

}

function printLog(str, cr = false) {
if (str) {
console.log((cr?'\n':'') + str)
pushStr += str + '\n'
}
}

function executeCommand(command) {
var info = { ctrl: false }
if (conf.debug) {
console.log('\nCommand received: ' + command)
}
executeKey(command, info)
}

function executeKey(key, info) {
if (key === 'l') {
listKeys()
} else if (key === 'b' && !info.ctrl ) {
engine.executeSignal('buy')
printLog('manual'.grey + ' limit ' + 'BUY'.green + ' command executed'.grey, true)
} else if (key === 'B' && !info.ctrl) {
engine.executeSignal('buy', null, null, false, true)
printLog('manual'.grey + ' market ' + 'BUY'.green + ' command executed'.grey, true)
} else if (key === 's' && !info.ctrl) {
engine.executeSignal('sell')
printLog('manual'.grey + ' limit ' + 'SELL'.red + ' command executed'.grey, true)
} else if (key === 'S' && !info.ctrl) {
engine.executeSignal('sell', null, null, false, true)
printLog('manual'.grey + ' market ' + 'SELL'.red + ' command executed'.grey, true)
} else if ((key === 'c' || key === 'C') && !info.ctrl) {
delete s.buy_order
delete s.sell_order
printLog('manual'.grey + ' order cancel' + ' command executed'.grey, true)
} else if (key === 'm' && !info.ctrl && so.mode === 'live') {
so.manual = !so.manual
printLog('MANUAL trade in LIVE mode: ' + (so.manual ? 'ON'.green.inverse : 'OFF'.red.inverse), true)
} else if (key === 'T' && !info.ctrl) {
so.order_type = 'taker'
printLog('Taker fees activated'.bgRed, true)
} else if (key === 'M' && !info.ctrl) {
so.order_type = 'maker'
printLog('Maker fees activated'.black.bgGreen, true)
} else if (key === 'o' && !info.ctrl) {
listOptions()
} else if (key === 'O' && !info.ctrl) {
printLog(cliff.inspect(so), true)
} else if (key === 'P' && !info.ctrl) {
printLog('Writing statistics...'.grey, true)
printTrade(false)
} else if (key === 'X' && !info.ctrl) {
printLog('Exiting... ' + '\nWriting statistics...'.grey, true)
printTrade(true)
} else if (key === 'd' && !info.ctrl) {
printLog('Dumping statistics...'.grey, true)
printTrade(false, true)
} else if (key === 'D' && !info.ctrl) {
printLog('Dumping statistics...'.grey, true)
toggleStats()
} else if (key === 'L' && !info.ctrl) {
debug.flip()
printLog('DEBUG mode: ' + (debug.on ? 'ON'.green.inverse : 'OFF'.red.inverse), true)
} else if (info.name === 'c' && info.ctrl) {
// @todo: cancel open orders before exit
process.exit()
}

if (pushStr) {
engine.pushMessage('Reply', colors.stripColors(pushStr))
pushStr = ''
}
}

var order_types = ['maker', 'taker']
if (!order_types.includes(so.order_type)) {
so.order_type = 'maker'
Expand Down Expand Up @@ -483,62 +553,13 @@ module.exports = function (program, conf) {

forwardScan()
setInterval(forwardScan, so.poll_trades)
if (!so.non_interactive) {
engine.onMessage(executeCommand)
}
readline.emitKeypressEvents(process.stdin)
if (!so.non_interactive && process.stdin.setRawMode) {
process.stdin.setRawMode(true)
process.stdin.on('keypress', function (key, info) {
if (key === 'l') {
listKeys()
} else if (key === 'b' && !info.ctrl ) {
engine.executeSignal('buy')
console.log('\nmanual'.grey + ' limit ' + 'BUY'.green + ' command executed'.grey)
} else if (key === 'B' && !info.ctrl) {
engine.executeSignal('buy', null, null, false, true)
console.log('\nmanual'.grey + ' market ' + 'BUY'.green + ' command executed'.grey)
} else if (key === 's' && !info.ctrl) {
engine.executeSignal('sell')
console.log('\nmanual'.grey + ' limit ' + 'SELL'.red + ' command executed'.grey)
} else if (key === 'S' && !info.ctrl) {
engine.executeSignal('sell', null, null, false, true)
console.log('\nmanual'.grey + ' market ' + 'SELL'.red + ' command executed'.grey)
} else if ((key === 'c' || key === 'C') && !info.ctrl) {
delete s.buy_order
delete s.sell_order
console.log('\nmanual'.grey + ' order cancel' + ' command executed'.grey)
} else if (key === 'm' && !info.ctrl && so.mode === 'live') {
so.manual = !so.manual
console.log('\nMANUAL trade in LIVE mode: ' + (so.manual ? 'ON'.green.inverse : 'OFF'.red.inverse))
} else if (key === 'T' && !info.ctrl) {
so.order_type = 'taker'
console.log('\n' + 'Taker fees activated'.bgRed)
} else if (key === 'M' && !info.ctrl) {
so.order_type = 'maker'
console.log('\n' + 'Maker fees activated'.black.bgGreen)
} else if (key === 'o' && !info.ctrl) {
listOptions()
} else if (key === 'O' && !info.ctrl) {
console.log('\n' + cliff.inspect(so))
} else if (key === 'P' && !info.ctrl) {
console.log('\nWriting statistics...'.grey)
printTrade(false)
} else if (key === 'X' && !info.ctrl) {
console.log('\nExiting... ' + '\nWriting statistics...'.grey)
printTrade(true)
} else if (key === 'd' && !info.ctrl) {
console.log('\nDumping statistics...'.grey)
printTrade(false, true)
} else if (key === 'D' && !info.ctrl) {
console.log('\nDumping statistics...'.grey)
toggleStats()
} else if (key === 'L' && !info.ctrl) {
debug.flip()
console.log('\nDEBUG mode: ' + (debug.on ? 'ON'.green.inverse : 'OFF'.red.inverse))
} else if (info.name === 'c' && info.ctrl) {
// @todo: cancel open orders before exit
console.log()
process.exit()
}
})
process.stdin.on('keypress', executeKey)
}
})
})
Expand Down
1 change: 1 addition & 0 deletions conf-sample.js
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,7 @@ c.notifiers.pushover.priority = '0' // choose a priority to send zenbot messages
// telegram configs
c.notifiers.telegram = {}
c.notifiers.telegram.on = false // false telegram disabled; true telegram enabled (key should be correct)
c.notifiers.telegram.interactive = false // true telegram is interactive
c.notifiers.telegram.bot_token = 'YOUR-BOT-TOKEN'
c.notifiers.telegram.chat_id = 'YOUR-CHAT-ID' // the id of the chat the messages should be send in
// end telegram configs
Expand Down
24 changes: 22 additions & 2 deletions extensions/notifiers/telegram.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,34 @@ process.env['NTBA_FIX_319'] = 1
var TelegramBot = require('node-telegram-bot-api')

module.exports = function telegram (config) {
var bot = new TelegramBot(config.bot_token, { polling: true })
var wrapper = function(cb) {
return function(message) {
if (message.chat.id != config.chat_id) {
console.log('\nChat ID error: command coming from wrong chat: ' + message.chat.id)
return
}
cb(message.text)
}
}
var telegram = {
pushMessage: function(title, message) {
var bot = new TelegramBot(config.bot_token)

bot.sendMessage(config.chat_id, title + ': ' + message).catch(function (error) {
console.error('\nerror: telegram notification')
console.log(error.response.body) // => { ok: false, error_code: 400, description: 'Bad Request: chat not found' }
})
},
onMessage: function (callback) {
bot.on('message', wrapper(callback))
bot.on('webhook_error', (error) => {
console.log('\nwebhook error: telegram event ' + error.code)
})
bot.on('polling_error', (error) => {
console.log('\npolling error: telegram event ' + error.code)
})
bot.on('error', (error) => {
console.log('\nerror: telegram event ' + error.code)
})
}
}
return telegram
Expand Down
8 changes: 8 additions & 0 deletions lib/engine.js
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,12 @@ module.exports = function (s, conf) {
}
}

function onMessage(callback) {
if (so.mode === 'live' || so.mode === 'paper') {
notifier.onMessage(callback)
}
}

function isFiat() {
return !s.currency.match(/^BTC|ETH|XMR|USDT$/)
}
Expand Down Expand Up @@ -996,6 +1002,8 @@ module.exports = function (s, conf) {
executeSignal: executeSignal,
writeReport: writeReport,
syncBalance: syncBalance,
pushMessage: pushMessage,
onMessage: onMessage,
}
}

20 changes: 18 additions & 2 deletions lib/notify.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,16 @@
module.exports = function notifier (conf) {
var active_notifiers = []
var interactive_notifiers = []

for (var notifier in conf.notifiers) {
if (conf.notifiers[notifier].on) {
active_notifiers.push(require(`../extensions/notifiers/${notifier}`)(conf.notifiers[notifier]))
var notif = require(`../extensions/notifiers/${notifier}`)(conf.notifiers[notifier])
notif.notifier_name = notifier

active_notifiers.push(notif)
if (conf.notifiers[notifier].interactive) {
interactive_notifiers.push(notif)
}
}
}

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

active_notifiers.forEach((notifier) => {
if (conf.debug) {
console.log(`Sending push message via ${notifier}`)
console.log(`Sending push message via ${notifier.notifier_name}`)
}
notifier.pushMessage(title, message)
})
},
onMessage: function (callback) {
interactive_notifiers.forEach((notifier) => {
if (conf.debug) {
console.log(`Receiving message from ${notifier.notifier_name}`)
}
notifier.onMessage(callback)
})
}
}
}