diff --git a/backend/src/backtest/index.ts b/backend/src/backtest/index.ts index 3e30f2a..dda746f 100644 --- a/backend/src/backtest/index.ts +++ b/backend/src/backtest/index.ts @@ -2,10 +2,19 @@ import { assertBacktestFeatureEnabled, assertBacktestMode } from './guards.js'; import { loadHistoricalData } from './data/loadHistoricalData.js'; import { runBacktestReplay } from './engine/BacktestRunner.js'; import { assertBacktestStrategyConfigSafe } from './strategySafety.js'; +import { withLogLevel } from '../utils/logger.js'; import type { BacktestRequest, BacktestResult } from './types.js'; export interface RunBacktestOptions { profileSettings?: any; + /** + * Override log verbosity for the duration of the backtest run. + * Default: 'warn' — silences ~5 info logs per candle that the strategy + * engine emits during normal operation. Pass 'info' or 'debug' when + * diagnosing engine behavior. + * See docs/backtest/ENGINE_READINESS.md §3.3. + */ + logLevel?: string; } export const runBacktest = async ( @@ -16,11 +25,12 @@ export const runBacktest = async ( assertBacktestMode(request.mode); assertBacktestStrategyConfigSafe(request.strategyConfig); const historical = await loadHistoricalData(request); - return runBacktestReplay({ + const level = options.logLevel ?? 'warn'; + return withLogLevel(level, () => runBacktestReplay({ request, dataset: historical.dataset, replayWindow: historical.window, dataSourceType: historical.source, profileSettings: options.profileSettings - }); + })); }; diff --git a/backend/src/utils/logger.ts b/backend/src/utils/logger.ts index 5bf8f28..4874933 100644 --- a/backend/src/utils/logger.ts +++ b/backend/src/utils/logger.ts @@ -1,19 +1,42 @@ -import winston from 'winston'; - -const logger = winston.createLogger({ - level: 'info', - format: winston.format.combine( - winston.format.timestamp(), - winston.format.json() - ), - transports: [ - new winston.transports.Console({ - format: winston.format.combine( - winston.format.colorize(), - winston.format.simple() - ), - }), - ], -}); - -export default logger; +import winston from 'winston'; + +// LOG_LEVEL env override — allows operators / tests / backtest to dial down +// noise without code changes. Defaults to 'info' to preserve existing behavior. +const initialLevel = process.env.LOG_LEVEL || 'info'; + +const logger = winston.createLogger({ + level: initialLevel, + format: winston.format.combine( + winston.format.timestamp(), + winston.format.json() + ), + transports: [ + new winston.transports.Console({ + format: winston.format.combine( + winston.format.colorize(), + winston.format.simple() + ), + }), + ], +}); + +/** + * Run a function with the logger temporarily set to a lower verbosity, then + * restore the prior level. Used by the backtest entry point so a 5,000-candle + * run doesn't emit ~25,000 log lines (see docs/backtest/ENGINE_READINESS.md + * §3.3). Safe across throws — the original level is always restored. + */ +export const withLogLevel = async ( + level: string, + fn: () => Promise | T +): Promise => { + const previous = logger.level; + logger.level = level; + try { + return await fn(); + } finally { + logger.level = previous; + } +}; + +export default logger;