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

Commit

Permalink
Enhance backtester: support all strageties and collect vs buy hold va…
Browse files Browse the repository at this point in the history
…lue (#344)

* support all strageties in backtester

* collect vs buy hold value in backtester

* Update backtester.js
  • Loading branch information
fasheng authored and DeviaVir committed Jul 3, 2017
1 parent fd80ea3 commit b1e650b
Showing 1 changed file with 116 additions and 22 deletions.
138 changes: 116 additions & 22 deletions scripts/auto_backtester/backtester.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,13 @@ let NEUTRAL_RATE_AUTO = false

let countArr = [];

let range = (start, end) => {
return Array(end - start + 1).fill().map((_, idx) => start + idx)
let range = (start, end, step) => {
if (!step) step = 1;
var r = [];
for (i=start; i<=end; i+=step) {
r = r.concat(i);
}
return r;
};

let product = args => {
Expand Down Expand Up @@ -66,8 +71,15 @@ let objectProduct = obj => {

let runCommand = (strategy, cb) => {
countArr.push(1);
let command = `zenbot sim ${simArgs} --trend_ema=${strategy.trend_ema} --oversold_rsi=${strategy.oversold_rsi} --oversold_rsi_periods=${strategy.oversold_rsi_periods} --neutral_rate=${strategy.neutral_rate}`;
console.log(`[ ${countArr.length}/${strategies.length} ] ${command}`);
let strategyArgs = {
macd: `--ema_short_period=${strategy.ema_short_period} --ema_long_period=${strategy.ema_long_period} --signal_period=${strategy.signal_period} --up_trend_threshold=${strategy.up_trend_threshold} --down_trend_threshold=${strategy.down_trend_threshold} --overbought_rsi_periods=${strategy.overbought_rsi_periods} --overbought_rsi=${strategy.overbought_rsi}`,
rsi: `--rsi_periods=${strategy.rsi_periods} --oversold_rsi=${strategy.oversold_rsi} --overbought_rsi=${strategy.overbought_rsi} --rsi_recover=${strategy.rsi_recover} --rsi_drop=${strategy.rsi_drop} --rsi_divisor=${strategy.rsi_divisor}`,
sar: `--sar_af=${strategy.sar_af} --sar_max_af=${strategy.sar_max_af}`,
speed: `--baseline_periods=${strategy.baseline_periods} --trigger_factor=${strategy.trigger_factor}`,
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 command = `node zenbot sim ${simArgs} ${strategyArgs[strategyName]}`;
console.log(`[ ${countArr.length}/${strategies[strategyName].length} ] ${command}`);

shell.exec(command, {silent:true, async:true}, (code, stdout, stderr) => {
if (code) {
Expand All @@ -82,14 +94,18 @@ let runCommand = (strategy, cb) => {
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 output2 = output.substr(output.length - 3000);

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 wins = parseInt(wlMatch[1]);
let losses = parseInt(wlMatch[2]);
Expand All @@ -104,13 +120,43 @@ let processOutput = output => {
return {
params: rawParams.replace(/[\r\n]/g, ''),
endBalance: parseFloat(endBalance),
buyHold: parseFloat(buyHold),
vsBuyHold: parseFloat(vsBuyHold),
wins: wins,
losses: losses,
errorRate: parseFloat(errorRate),
trendEma: params.trend_ema,

// 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,

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

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

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

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

days: days,
period: params.period,
roi: roi,
Expand All @@ -119,27 +165,59 @@ let processOutput = output => {
};
};

let strategies = objectProduct({
trend_ema: range(TREND_EMA_MIN, TREND_EMA_MAX),
oversold_rsi: range(OVERSOLD_RSI_MIN, OVERSOLD_RSI_MAX),
oversold_rsi_periods: range(OVERSOLD_RSI_PERIODS_MIN, OVERSOLD_RSI_PERIODS_MAX),
neutral_rate: (NEUTRAL_RATE_AUTO ? new Array('auto') : []).concat(range(NEUTRAL_RATE_MIN, NEUTRAL_RATE_MAX).map(r => r / 100))
});

let tasks = strategies.map(strategy => {
return cb => {
runCommand(strategy, cb)
}
});
let strategies = {
macd: objectProduct({
ema_short_period: range(10, 15),
ema_long_period: range(20, 30),
signal_period: range(9, 9),
up_trend_threshold: range(0, 0),
down_trend_threshold: range(0, 0),
overbought_rsi_periods: range(15, 25),
overbought_rsi: range(70, 70)
}),
rsi: objectProduct({
rsi_periods: range(10, 30),
oversold_rsi: range(20, 35),
overbought_rsi: range(82, 82),
rsi_recover: range(3, 3),
rsi_drop: range(0, 0),
rsi_divisor: range(2, 2)
}),
sar: objectProduct({
sar_af: range(0.01, 0.055, 0.005),
sar_max_af: range(0.1, 0.55, 0.05)
}),
speed: objectProduct({
baseline_periods: range(1000, 5000, 200),
trigger_factor: range(1.0, 2.0, 0.1)
}),
trend_ema: objectProduct({
trend_ema: range(TREND_EMA_MIN, TREND_EMA_MAX),
neutral_rate: (NEUTRAL_RATE_AUTO ? new Array('auto') : []).concat(range(NEUTRAL_RATE_MIN, NEUTRAL_RATE_MAX).map(r => r / 100)),
oversold_rsi_periods: range(OVERSOLD_RSI_PERIODS_MIN, OVERSOLD_RSI_PERIODS_MAX),
oversold_rsi: range(OVERSOLD_RSI_MIN, OVERSOLD_RSI_MAX)
})
};

let args = process.argv;
args.shift();
args.shift();
let simArgs = args.join(' ');
let strategyName = 'trend_ema';
if (args.indexOf('--strategy') !== -1) {
strategyName = args[args.indexOf('--strategy') + 1];
}

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

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

parallel(tasks, PARALLEL_LIMIT, (err, results) => {
console.log("\nBacktesting complete, saving results...");
Expand All @@ -148,10 +226,26 @@ parallel(tasks, PARALLEL_LIMIT, (err, results) => {
})
results.sort((a,b) => (a.roi < b.roi) ? 1 : ((b.roi < a.roi) ? -1 : 0));
let fileName = `backtesting_${Math.round(+new Date()/1000)}.csv`;
let filedsGeneral = ['roi', 'vsBuyHold', 'errorRate', 'wlRatio', 'frequency', 'endBalance', 'buyHold', 'wins', 'losses', 'period', 'days'];
let filedNamesGeneral = ['ROI (%)', 'VS Buy Hold (%)', 'Error Rate (%)', 'Win/Loss Ratio', '# Trades/Day', 'Ending Balance ($)', 'Buy Hold ($)', '# Wins', '# Losses', 'Period', '# Days'];
let fields = {
macd: filedsGeneral.concat([ 'emaShortPeriod', 'emaLongPeriod', 'signalPeriod', 'upTrendThreshold', 'downTrendThreshold', 'overboughtRsiPeriods', 'overboughtRsi', 'params']),
rsi: filedsGeneral.concat(['rsiPeriods', 'oversoldRsi', 'overboughtRsi', 'rsiRecover', 'rsiDrop', 'rsiDivsor', 'params']),
sar: filedsGeneral.concat(['sarAf', 'sarMaxAf', 'params']),
speed: filedsGeneral.concat(['baselinePeriods', 'triggerFactor', 'params']),
trend_ema: filedsGeneral.concat(['trendEma', 'neutralRate', 'oversoldRsiPeriods', 'oversoldRsi', 'params'])
};
let fieldNames = {
macd: filedNamesGeneral.concat(['EMA Short Period', 'EMA Long Period', 'Signal Period', 'Up Trend Threshold', 'Down Trend Threshold', 'Overbought Rsi Periods', 'Overbought Rsi', 'Full Parameters']),
rsi: filedNamesGeneral.concat(['RSI Periods', 'Oversold RSI', 'Overbought RSI', 'RSI Recover', 'RSI Drop', 'RSI Divisor', 'Full Parameters']),
sar: filedNamesGeneral.concat(['SAR AF', 'SAR MAX AF', 'Full Parameters']),
speed: filedNamesGeneral.concat(['Baseline Periods', 'Trigger Factor', 'Full Parameters']),
trend_ema: filedNamesGeneral.concat(['Trend EMA', 'Neutral Rate', 'Oversold RSI Periods', 'Oversold RSI', 'Full Parameters'])
};
let csv = json2csv({
data: results,
fields: ['roi', 'errorRate', 'wlRatio', 'frequency', 'endBalance', 'wins', 'losses', 'period', 'days', 'trendEma', 'oversoldRsi', 'oversoldRsiPeriods', 'neutralRate', 'params'],
fieldNames: ['ROI (%)', 'Error Rate (%)', 'Win/Loss Ratio', '# Trades/Day', 'Ending Balance ($)', '# Wins', '# Losses', 'Period', '# Days', 'Trend EMA', 'Oversold RSI', 'Oversold RSI Periods', 'Neutral Rate', 'Full Parameters']
fields: fields[strategyName],
fieldNames: fieldNames[strategyName]
});

fs.writeFile(fileName, csv, err => {
Expand Down

0 comments on commit b1e650b

Please sign in to comment.