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

Output json result to file during simulation #1206

Merged
merged 19 commits into from
Jan 24, 2018
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
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,5 @@ logs
dist/*
.idea
*.iml
simulations/sim_*.json

37 changes: 32 additions & 5 deletions commands/sim.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ module.exports = function container (get, set, clear) {
.option('--rsi_periods <periods>', 'number of periods to calculate RSI at', Number, c.rsi_periods)
.option('--disable_options', 'disable printing of options')
.option('--enable_stats', 'enable printing order stats')
.option('--backtester_generation <generation>','creates a json file in simulations with the generation number', Number, -1)
.option('--verbose', 'print status lines on every period')
.action(function (selector, cmd) {
var s = {options: minimist(process.argv)}
Expand Down Expand Up @@ -72,6 +73,7 @@ module.exports = function container (get, set, clear) {
so.verbose = !!cmd.verbose
so.selector = get('lib.objectify-selector')(selector || c.selector)
so.mode = 'sim'

if (cmd.conf) {
var overrides = require(path.resolve(process.cwd(), cmd.conf))
Object.keys(overrides).forEach(function (k) {
Expand All @@ -82,7 +84,7 @@ module.exports = function container (get, set, clear) {
if (!so.min_periods) so.min_periods = 1
var cursor, reversing, reverse_point
var query_start = so.start ? tb(so.start).resize(so.period_length).subtract(so.min_periods + 2).toMilliseconds() : null

function exitSim () {
console.log()
if (!s.period) {
Expand All @@ -99,10 +101,10 @@ module.exports = function container (get, set, clear) {
option_keys.forEach(function (k) {
options[k] = so[k]
})
if (so.show_options) {
var options_json = JSON.stringify(options, null, 2)
output_lines.push(options_json)
}

let options_output = options
options_output.simresults = {}

if (s.my_trades.length) {
s.my_trades.push({
price: s.period.close,
Expand All @@ -112,6 +114,7 @@ module.exports = function container (get, set, clear) {
})
}
s.balance.currency = n(s.balance.currency).add(n(s.period.close).multiply(s.balance.asset)).format('0.00000000')

s.balance.asset = 0
s.lookback.unshift(s.period)
var profit = s.start_capital ? n(s.balance.currency).subtract(s.start_capital).divide(s.start_capital) : n(0)
Expand Down Expand Up @@ -142,9 +145,32 @@ module.exports = function container (get, set, clear) {
output_lines.push('win/loss: ' + (sells - losses) + '/' + losses)
output_lines.push('error rate: ' + (sells ? n(losses).divide(sells).format('0.00%') : '0.00%').yellow)
}
options_output.simresults.start_capital = s.start_capital
options_output.simresults.currency = n(s.balance.currency).value()
options_output.simresults.profit = profit.value()
options_output.simresults.buy_hold = buy_hold.value()
options_output.simresults.buy_hold_profit = buy_hold_profit.value()
options_output.simresults.total_trades = s.my_trades.length
options_output.simresults.length_days = s.day_count
options_output.simresults.total_sells = sells
options_output.simresults.total_losses = losses
options_output.simresults.vs_buy_hold = n(s.balance.currency).subtract(buy_hold).divide(buy_hold).value() * 100.00

let options_json = JSON.stringify(options_output, null, 2)
if (so.show_options) {
output_lines.push(options_json)
}

output_lines.forEach(function (line) {
console.log(line)
})

if (so.backtester_generation >= 0)
{
fs.writeFileSync(path.resolve(__dirname, '..', 'simulations','sim_'+so.strategy.replace('_','')+'_'+ so.selector.normalized.replace('_','').toLowerCase()+'_'+so.backtester_generation+'.json'),options_json, {encoding: 'utf8'})
}


if (so.filename !== 'none') {
var html_output = output_lines.map(function (line) {
return colors.stripColors(line)
Expand All @@ -169,6 +195,7 @@ module.exports = function container (get, set, clear) {
fs.writeFileSync(out_target, out)
console.log('wrote', out_target)
}

process.exit(0)
}

Expand Down
176 changes: 100 additions & 76 deletions scripts/auto_backtester/backtester.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,12 @@ let parallel = require('run-parallel-limit');
let json2csv = require('json2csv');
let roundp = require('round-precision');
let fs = require('fs');
let path = require('path')
let StripAnsi = require('strip-ansi');

let VERSION = 'Zenbot 4.04 Backtester v0.2';

let PARALLEL_LIMIT = require('os').cpus().length;
let PARALLEL_LIMIT = (process.env.PARALLEL_LIMIT && +process.env.PARALLEL_LIMIT) || require('os').cpus().length;

let TREND_EMA_MIN = 20;
let TREND_EMA_MAX = 20;
Expand Down Expand Up @@ -70,7 +71,7 @@ let objectProduct = obj => {
});
};

let runCommand = (strategy, cb) => {
let runCommand = (strategy, exchangeMarketPair,strategyName, cb) => {
countArr.push(1);
let strategyArgs = {
cci_srsi: `--cci_periods=${strategy.rsi_periods} --rsi_periods=${strategy.srsi_periods} --srsi_periods=${strategy.srsi_periods} --srsi_k=${strategy.srsi_k} --srsi_d=${strategy.srsi_d} --oversold_rsi=${strategy.oversold_rsi} --overbought_rsi=${strategy.overbought_rsi} --oversold_cci=${strategy.oversold_cci} --overbought_cci=${strategy.overbought_cci} --constant=${strategy.constant}`,
Expand All @@ -82,49 +83,48 @@ let runCommand = (strategy, cb) => {
trend_ema: `--trend_ema=${strategy.trend_ema} --oversold_rsi=${strategy.oversold_rsi} --oversold_rsi_periods=${strategy.oversold_rsi_periods} --neutral_rate=${strategy.neutral_rate}`
};
let zenbot_cmd = process.platform === 'win32' ? 'zenbot.bat' : './zenbot.sh'; // Use 'win32' for 64 bit windows too
let command = `${zenbot_cmd} sim ${simArgs} ${strategyArgs[strategyName]} --period_length=${strategy.period_length} --min_periods=${strategy.min_periods}`;
console.log(`[ ${countArr.length}/${strategies[strategyName].length} ] ${command}`);
let localGen = countArr.length
strategy.backtester_generation = localGen

let command = `${zenbot_cmd} sim ${simArgs} ${strategyArgs[strategyName]} --period_length=${strategy.period_length} --min_periods=${strategy.min_periods} --backtester_generation=${localGen}`;
console.log(`[ ${localGen}/${strategies[strategyName].length} ] ${command}`);

shell.exec(command, {silent:true, async:true}, (code, stdout, stderr) => {
if (code) {
console.error(command)
console.error(stderr)
return cb(null, null)
}
cb(null, processOutput(stdout));
cb(null, processOutput(stdout,localGen,strategyName, exchangeMarketPair));
});
};

let processOutput = output => {
let jsonRegexp = /(\{[\s\S]*?\})\send balance/g;
let endBalRegexp = /end balance: (\d+\.\d+) \(/g;
let buyHoldRegexp = /buy hold: (\d+\.\d+) \(/g;
let vsBuyHoldRegexp = /vs. buy hold: (-?\d+\.\d+)%/g;
let wlRegexp = /win\/loss: (\d+)\/(\d+)/g;
let errRegexp = /error rate: (.*)%/g;

let strippedOutput = StripAnsi(output);
let output2 = strippedOutput.substr(strippedOutput.length - 3500);

let rawParams = jsonRegexp.exec(output2)[1];
let params = JSON.parse(rawParams);
let endBalance = endBalRegexp.exec(output2)[1];
let buyHold = buyHoldRegexp.exec(output2)[1];
let vsBuyHold = vsBuyHoldRegexp.exec(output2)[1];
let wlMatch = wlRegexp.exec(output2);
let errMatch = errRegexp.exec(output2);
let wins = wlMatch !== null ? parseInt(wlMatch[1]) : 0;
let losses = wlMatch !== null ? parseInt(wlMatch[2]) : 0;
let errorRate = errMatch !== null ? parseInt(errMatch[1]) : 0;
let days = parseInt(params.days);
let processOutput = (output,generation,strategyName, exchangeMarketPair) => {

let tFileName = path.resolve(__dirname, '..','..', 'simulations','sim_'+strategyName.replace('_','')+'_'+exchangeMarketPair.replace(' ','')+'_'+generation+'.json')
let simulationResults
if (fs.existsSync(tFileName))
{
let jsonBuffer
jsonBuffer = fs.readFileSync(tFileName,{encoding:'utf8'})
simulationResults = JSON.parse(jsonBuffer)
fs.unlinkSync(tFileName)
}

let endBalance = simulationResults.simresults.currency
let buyHold = simulationResults.simresults.buy_hold
let vsBuyHold = simulationResults.simresults.vs_buy_hold
let wins = simulationResults.simresults.total_sells
let losses = simulationResults.simresults.total_losses
let errorRate = simulationResults.simresults.total_losses / simulationResults.simresults.total_sells
let days = parseInt(simulationResults.days)
let roi = roundp(
((endBalance - params.currency_capital) / params.currency_capital) * 100,
((endBalance - simulationResults.currency_capital) / simulationResults.currency_capital) * 100,
3
);
)

return {
params: rawParams.replace(/[\r\n]/g, ''),
params: JSON.stringify(simulationResults),
endBalance: parseFloat(endBalance),
buyHold: parseFloat(buyHold),
vsBuyHold: parseFloat(vsBuyHold),
Expand All @@ -133,64 +133,64 @@ let processOutput = output => {
errorRate: parseFloat(errorRate),

// cci_srsi
cciPeriods: params.cci_periods,
rsiPeriods: params.rsi_periods,
srsiPeriods: params.srsi_periods,
srsiK: params.srsi_k,
srsiD: params.srsi_d,
oversoldRsi: params.oversold_rsi,
overboughtRsi: params.overbought_rsi,
oversoldCci: params.oversold_cci,
overboughtCci: params.overbought_cci,
constant: params.consant,
cciPeriods: simulationResults.cci_periods,
rsiPeriods: simulationResults.rsi_periods,
srsiPeriods: simulationResults.srsi_periods,
srsiK: simulationResults.srsi_k,
srsiD: simulationResults.srsi_d,
oversoldRsi: simulationResults.oversold_rsi,
overboughtRsi: simulationResults.overbought_rsi,
oversoldCci: simulationResults.oversold_cci,
overboughtCci: simulationResults.overbought_cci,
constant: simulationResults.consant,

// srsi_macd
rsiPeriods: params.rsi_periods,
srsiPeriods: params.srsi_periods,
srsiK: params.srsi_k,
srsiD: params.srsi_d,
oversoldRsi: params.oversold_rsi,
overboughtRsi: params.overbought_rsi,
emaShortPeriod: params.ema_short_period,
emaLongPeriod: params.ema_long_period,
signalPeriod: params.signal_period,
upTrendThreshold: params.up_trend_threshold,
downTrendThreshold: params.down_trend_threshold,
rsiPeriods: simulationResults.rsi_periods,
srsiPeriods: simulationResults.srsi_periods,
srsiK: simulationResults.srsi_k,
srsiD: simulationResults.srsi_d,
oversoldRsi: simulationResults.oversold_rsi,
overboughtRsi: simulationResults.overbought_rsi,
emaShortPeriod: simulationResults.ema_short_period,
emaLongPeriod: simulationResults.ema_long_period,
signalPeriod: simulationResults.signal_period,
upTrendThreshold: simulationResults.up_trend_threshold,
downTrendThreshold: simulationResults.down_trend_threshold,

// macd
emaShortPeriod: params.ema_short_period,
emaLongPeriod: params.ema_long_period,
signalPeriod: params.signal_period,
upTrendThreshold: params.up_trend_threshold,
downTrendThreshold: params.down_trend_threshold,
overboughtRsiPeriods: params.overbought_rsi_periods,
overboughtRsi: params.overbought_rsi,
emaShortPeriod: simulationResults.ema_short_period,
emaLongPeriod: simulationResults.ema_long_period,
signalPeriod: simulationResults.signal_period,
upTrendThreshold: simulationResults.up_trend_threshold,
downTrendThreshold: simulationResults.down_trend_threshold,
overboughtRsiPeriods: simulationResults.overbought_rsi_periods,
overboughtRsi: simulationResults.overbought_rsi,

// rsi
rsiPeriods: params.rsi_periods,
oversoldRsi: params.oversold_rsi,
overboughtRsi: params.overbought_rsi,
rsiRecover: params.rsi_recover,
rsiDrop: params.rsi_drop,
rsiDivsor: params.rsi_divisor,
rsiPeriods: simulationResults.rsi_periods,
oversoldRsi: simulationResults.oversold_rsi,
overboughtRsi: simulationResults.overbought_rsi,
rsiRecover: simulationResults.rsi_recover,
rsiDrop: simulationResults.rsi_drop,
rsiDivsor: simulationResults.rsi_divisor,

// sar
sarAf: params.sar_af,
sarMaxAf: params.sar_max_af,
sarAf: simulationResults.sar_af,
sarMaxAf: simulationResults.sar_max_af,

// speed
baselinePeriods: params.baseline_periods,
triggerFactor: params.trigger_factor,
baselinePeriods: simulationResults.baseline_periods,
triggerFactor: simulationResults.trigger_factor,

// trend_ema
trendEma: params.trend_ema,
neutralRate: params.neutral_rate,
oversoldRsiPeriods: params.oversold_rsi_periods,
oversoldRsi: params.oversold_rsi,
trendEma: simulationResults.trend_ema,
neutralRate: simulationResults.neutral_rate,
oversoldRsiPeriods: simulationResults.oversold_rsi_periods,
oversoldRsi: simulationResults.oversold_rsi,

days: days,
period_length: params.period_length,
min_periods: params.min_periods,
period_length: simulationResults.period_length,
min_periods: simulationResults.min_periods,
roi: roi,
wlRatio: losses > 0 ? roundp(wins / losses, 3) : 'Infinity',
frequency: roundp((wins + losses) / days, 3)
Expand Down Expand Up @@ -270,6 +270,7 @@ let strategies = {
};

let args = process.argv;
let exchangeMarketPair = args[2].toLowerCase();
args.shift();
args.shift();
let simArgs = args.join(' ');
Expand All @@ -280,16 +281,39 @@ if (args.indexOf('--strategy') !== -1) {

let tasks = strategies[strategyName].map(strategy => {
return cb => {
runCommand(strategy, cb)
runCommand(strategy,exchangeMarketPair,strategyName.toLowerCase(), cb)
}
});

console.log(`\n--==${VERSION}==--`);
console.log(new Date().toUTCString());
console.log(`\nBacktesting [${strategies[strategyName].length}] iterations for strategy ${strategyName}...\n`);

//Clean up any generation files left over in the simulation directory
//they will be overwritten, but best not to confuse the issue.
//if it fails. doesn't matter they will be overwritten anyways. not need to halt the system.
try
{
let tDirName = path.resolve(__dirname, '..','..', 'simulations')
let tFileName = 'sim_'
let files = fs.readdirSync(tDirName)

for(let i = 0; i < files.length; i++)
{
if (files[i].lastIndexOf(tFileName) == 0)
{
let filePath = path.resolve(__dirname, '..','..', 'simulations',files[i] )
fs.unlinkSync(filePath)
}

}
} catch (err)
{
console.log('error deleting lint from prior run')
}

parallel(tasks, PARALLEL_LIMIT, (err, results) => {
console.log("\nBacktesting complete, saving results...");
console.log('\nBacktesting complete, saving results...');
results = results.filter(function (r) {
return !!r
})
Expand Down
Loading