Skip to content
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
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -562,6 +562,17 @@ https://www.prowlapp.com/
Supply zenbot with your TextBelt API key and zenbot will send SMS notifications to your cell phone.
https://www.textbelt.com/

## Rest API

You can enable a Rest API for Zenbot by enabling the following configuration
```
c.output.api = {}
c.output.api.on = true
c.output.api.port = 0 // 0 = random port
```
You can choose a port, or pick 0 for a random port.

Once you did that, you can call the API on: http://\<hostname\>:\<port\>/trades

## Manual trade tools

Expand Down
116 changes: 111 additions & 5 deletions commands/trade.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,8 @@ module.exports = function container (get, set, clear) {
process.exit(1)
}
var engine = get('lib.engine')(s)

get('lib.output').initializeOutput(s)

const keyMap = new Map()
keyMap.set('b', 'limit'.grey + ' BUY'.green)
keyMap.set('B', 'market'.grey + ' BUY'.green)
Expand All @@ -86,6 +87,8 @@ module.exports = function container (get, set, clear) {
keyMap.set('O', 'show current trade options in a dirty view (full list)'.grey)
keyMap.set('P', 'print statistical output'.grey)
keyMap.set('X', 'exit program with statistical output'.grey)
keyMap.set('d', 'dump statistical output to HTML file'.grey)
keyMap.set('D', 'toggle automatic HTML dump to file'.grey)

function listKeys() {
console.log('\nAvailable command keys:')
Expand Down Expand Up @@ -131,7 +134,7 @@ module.exports = function container (get, set, clear) {
}

/* Implementing statistical Exit */
function printTrade (quit) {
function printTrade (quit, dump) {
console.log()
var output_lines = []
var tmp_balance = n(s.balance.currency).add(n(s.period.close).multiply(s.balance.asset)).format('0.00000000')
Expand Down Expand Up @@ -175,7 +178,7 @@ module.exports = function container (get, set, clear) {
output_lines.forEach(function (line) {
console.log(line)
})
if (quit) {
if (quit || dump) {
var html_output = output_lines.map(function (line) {
return colors.stripColors(line)
}).join('\n')
Expand All @@ -198,12 +201,108 @@ module.exports = function container (get, set, clear) {
.replace('{{output}}', html_output)
.replace(/\{\{symbol\}\}/g, so.selector + ' - zenbot ' + require('../package.json').version)
if (so.filename !== 'none') {
var out_target = so.filename || 'simulations/trade_result_' + so.selector +'_' + new Date().toISOString().replace(/T/, '_').replace(/\..+/, '').replace(/-/g, '').replace(/:/g, '').replace(/20/, '') + '_UTC.html'
var out_target

if(dump){
var dt = new Date().toISOString();

//ymd
var today = dt.slice(2, 4) + dt.slice(5, 7) + dt.slice(8, 10);
out_target = so.filename || 'simulations/trade_result_' + so.selector +'_' + today + '_UTC.html'
fs.writeFileSync(out_target, out)
}else
out_target = so.filename || 'simulations/trade_result_' + so.selector +'_' + new Date().toISOString().replace(/T/, '_').replace(/\..+/, '').replace(/-/g, '').replace(/:/g, '').replace(/20/, '') + '_UTC.html'

fs.writeFileSync(out_target, out)
console.log('\nwrote'.grey, out_target)
}
process.exit(0)
if(quit) process.exit(0)
}
}
/* The end of printTrade */

/* Implementing statistical status dump every 10 secs */
var shouldSaveStats = false;
function toggleStats(){
shouldSaveStats = !shouldSaveStats;
if(shouldSaveStats)
console.log("Auto stats dump enabled")
else
console.log("Auto stats dump disabled")
}

function saveStatsLoop(){
saveStats()
setTimeout(function () {
saveStatsLoop()
}, 10000)
}
saveStatsLoop()

function saveStats () {
if(!shouldSaveStats) return;

var output_lines = []
var tmp_balance = n(s.balance.currency).add(n(s.period.close).multiply(s.balance.asset)).format('0.00000000')

var profit = s.start_capital ? n(tmp_balance).subtract(s.start_capital).divide(s.start_capital) : n(0)
output_lines.push('last balance: ' + n(tmp_balance).format('0.00000000').yellow + ' (' + profit.format('0.00%') + ')')
var buy_hold = s.start_price ? n(s.period.close).multiply(n(s.start_capital).divide(s.start_price)) : n(tmp_balance)
var buy_hold_profit = s.start_capital ? n(buy_hold).subtract(s.start_capital).divide(s.start_capital) : n(0)
output_lines.push('buy hold: ' + buy_hold.format('0.00000000').yellow + ' (' + n(buy_hold_profit).format('0.00%') + ')')
output_lines.push('vs. buy hold: ' + n(tmp_balance).subtract(buy_hold).divide(buy_hold).format('0.00%').yellow)
output_lines.push(s.my_trades.length + ' trades over ' + s.day_count + ' days (avg ' + n(s.my_trades.length / s.day_count).format('0.00') + ' trades/day)')
var last_buy
var losses = 0, sells = 0
s.my_trades.forEach(function (trade) {
if (trade.type === 'buy') {
last_buy = trade.price
}
else {
if (last_buy && trade.price < last_buy) {
losses++
}
sells++
}
})
if (s.my_trades.length && sells > 0) {
output_lines.push('win/loss: ' + (sells - losses) + '/' + losses)
output_lines.push('error rate: ' + (sells ? n(losses).divide(sells).format('0.00%') : '0.00%').yellow)
}

var html_output = output_lines.map(function (line) {
return colors.stripColors(line)
}).join('\n')
var data = s.lookback.slice(0, s.lookback.length - so.min_periods).map(function (period) {
return {
time: period.time,
open: period.open,
high: period.high,
low: period.low,
close: period.close,
volume: period.volume
}
})
var code = 'var data = ' + JSON.stringify(data) + ';\n'
code += 'var trades = ' + JSON.stringify(s.my_trades) + ';\n'
var tpl = fs.readFileSync(path.resolve(__dirname, '..', 'templates', 'sim_result.html.tpl'), {encoding: 'utf8'})
var out = tpl
.replace('{{code}}', code)
.replace('{{trend_ema_period}}', so.trend_ema || 36)
.replace('{{output}}', html_output)
.replace(/\{\{symbol\}\}/g, so.selector + ' - zenbot ' + require('../package.json').version)
if (so.filename !== 'none') {
var out_target
var dt = new Date().toISOString();

//ymd
var today = dt.slice(2, 4) + dt.slice(5, 7) + dt.slice(8, 10);
out_target = so.filename || 'simulations/trade_result_' + so.selector +'_' + today + '_UTC.html'

fs.writeFileSync(out_target, out)
//console.log('\nwrote'.grey, out_target)
}

}
/* The end of printTrade */

Expand Down Expand Up @@ -336,6 +435,13 @@ module.exports = function container (get, set, clear) {
} 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 (info.name === 'c' && info.ctrl) {
// @todo: cancel open orders before exit
console.log()
Expand Down
8 changes: 8 additions & 0 deletions conf-sample.js
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ c.quadriga.secret = 'YOUR-SECRET'
c.quadriga.client_id = 'YOUR-CLIENT-ID'

// to enable WEX.NZ trading, enter your API credentials:
// Note: WexNZ only supports backfilling the last ~1/4 day ATM.
c.wexnz = {}
c.wexnz.key = 'YOUR-API-KEY'
c.wexnz.secret = 'YOUR-SECRET'
Expand Down Expand Up @@ -205,3 +206,10 @@ c.notifiers.textbelt.phone = '3121234567'
c.notifiers.textbelt.key = 'textbelt'
// end textbelt configs

// output
c.output = {}

// REST API
c.output.api = {}
c.output.api.on = true
c.output.api.port = 0 // 0 = random port
6 changes: 6 additions & 0 deletions extensions/exchanges/binance/_codemap.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
module.exports = {
_ns: 'zenbot',

'exchanges.binance': require('./exchange'),
'exchanges.list[]': '#exchanges.binance'
}
Loading