@gabriel3615/ta_analysis
Version:
stock ta analysis
58 lines (57 loc) • 2.14 kB
JavaScript
import { Backtester, } from './Backtester.js';
function cartesianProduct(arrays) {
return arrays.reduce((acc, arr) => {
const next = [];
for (const a of acc) {
for (const b of arr) {
next.push([...a, b]);
}
}
return next;
}, [[]]);
}
function buildParamCombos(grid) {
const keys = Object.keys(grid);
if (keys.length === 0)
return [{}];
const values = keys.map(k => grid[k]);
const combos = cartesianProduct(values);
return combos.map(vals => {
const obj = {};
vals.forEach((v, idx) => (obj[keys[idx]] = v));
return obj;
});
}
export async function runGridSearch(candles, grid, strategyFactory, backtesterInstance // Allow passing a pre-configured backtester
) {
const paramCombos = buildParamCombos(grid);
const runs = [];
for (const params of paramCombos) {
const strategy = strategyFactory(params);
const backtester = backtesterInstance ?? new Backtester(); // Use provided instance or create a default one
const result = await backtester.run(candles, strategy);
runs.push({ params, result });
}
const bestByTotalPnL = runs.length
? runs.reduce((a, b) => (a.result.totalPnL > b.result.totalPnL ? a : b))
: null;
const bestBySharpe = runs.length
? runs.reduce((a, b) => a.result.metrics.sharpe > b.result.metrics.sharpe ? a : b)
: null;
const bestByMaxDrawdown = runs.length
? runs.reduce((a, b) => a.result.metrics.maxDrawdown < b.result.metrics.maxDrawdown ? a : b)
: null;
return { runs, summary: { bestByTotalPnL, bestBySharpe, bestByMaxDrawdown } };
}
export function formatMetricsPanel(run) {
const m = run.result.metrics;
return [
`PnL: ${run.result.totalPnL.toFixed(2)}`,
`WinRate: ${(m.winRate * 100).toFixed(2)}%`,
`AvgWin: ${m.avgWin.toFixed(2)}`,
`AvgLoss: ${m.avgLoss.toFixed(2)}`,
`MaxDD: ${(m.maxDrawdown * 100).toFixed(2)}%`,
`Sharpe: ${m.sharpe.toFixed(2)}`,
`Trades: ${m.tradeCount}`,
].join(' | ');
}