""" QuantEngine.jl — Top-level module. Only file that uses include(). Wires all submodules together by injecting dependencies explicitly. Python imports this via juliacall. """ module QuantEngine using Statistics, Random # ── Include all submodules (ONLY here) ─────────────── include("Indicators.jl") include("BacktestEngine.jl") include("Optimizer.jl") include("SignalCompiler.jl") using .Indicators using .BacktestEngine using .Optimizer using .SignalCompiler export # Indicators sma, ema, wma, tema, dema, rsi, macd, stoch, cci, williams_r, atr, bbands, keltner, donchian, adx, vwap, obv, cmf, zscore, std_dev, momentum, roc, highest, lowest, crossover, crossunder, # Engine BacktestConfig, run_backtest, # High-level full_backtest_pipeline """ full_backtest_pipeline(...) -> Dict{String,Any} End-to-end: compile Julia strategy code → walk-forward optimize → return plain Dict that crosses to Python cleanly. """ function full_backtest_pipeline( strategy_code::String, strategy_name::String, open_p::Vector{Float64}, high::Vector{Float64}, low::Vector{Float64}, close::Vector{Float64}, volume::Vector{Float64}, timeframe::String, symbol::String; n_windows::Int=5, is_ratio::Float64=0.70, min_trades::Int=30, min_sharpe::Float64=0.5, max_combos::Int=300, initial_equity::Float64=10_000.0, commission_pct::Float64=0.0002, risk_per_trade::Float64=0.01, )::Dict{String,Any} # 1. Compile strategy — pass Indicators module explicitly compiled = SignalCompiler.compile_strategy(strategy_name, strategy_code, Indicators) if !compiled.is_valid return Dict{String,Any}("is_valid"=>false,"error"=>compiled.error, "strategy"=>strategy_name,"symbol"=>symbol,"timeframe"=>timeframe) end # 2. Walk-forward optimize — inject BacktestEngine functions to avoid circular deps cfg_fn = () -> BacktestConfig( initial_equity=initial_equity, commission_pct=commission_pct, risk_per_trade=risk_per_trade, ) # Wrap run_backtest to inject atr function from Indicators run_bt_fn = (o,h,l,c,v,sigs,tf,cfg) -> BacktestEngine.run_backtest(o,h,l,c,v,sigs,tf,cfg, Indicators.atr) opt = Optimizer.walk_forward_optimize( compiled.generate_fn, compiled.param_grid_fn(), open_p, high, low, close, volume, timeframe, strategy_name, symbol; run_bt_fn=run_bt_fn, bt_cfg_fn=cfg_fn, n_windows=n_windows, is_ratio=is_ratio, min_trades=min_trades, min_sharpe=min_sharpe, max_combos=max_combos, ) return Dict{String,Any}( "is_valid" => true, "strategy" => opt.strategy_name, "symbol" => opt.symbol, "timeframe" => opt.timeframe, "optimal_params" => opt.optimal_params, "oos_sharpe_mean" => opt.oos_sharpe_mean, "oos_sharpe_std" => opt.oos_sharpe_std, "oos_win_rate" => opt.oos_win_rate, "oos_max_dd" => opt.oos_max_dd, "oos_pf_mean" => opt.oos_pf_mean, "oos_trades" => opt.oos_trades, "wf_efficiency" => opt.wf_efficiency, "robustness" => opt.robustness, "is_viable" => opt.is_viable, "reasons" => opt.reasons, "oos_sharpes" => opt.oos_sharpes, ) end end # module QuantEngine