aether_quant.backtest¶
Backtests, grouped by the job (V6.6.5): backtest() for any weights on any prices, the project's own rank-book and target-weights simulators, Lean-exact statistics and Monte Carlo.
Backtests, grouped by the job (V6.6.5): backtest() for any weights on any prices, the project's own rank-book and
target-weights simulators, Lean-exact statistics and Monte Carlo.
BacktestResult
dataclass
¶
Daily series of one path. returns are net of costs, gross_returns before them; costs and turnover are
fractions of the previous NAV; weights are the end-of-day holdings.
report ¶
report(*, title: str = 'Backtest', risk_free: float = 0.0) -> str
A plain-text summary table.
MarketPanel
dataclass
¶
Bars by calendar date (a NaN where the ticker has no bar that day). sessions are the equity sessions.
backtest ¶
backtest(weights: DataFrame, prices: DataFrame, *, opens: DataFrame | None = None, cost_model: CostModel | None = None, fill: Literal['next_open', 'close'] = 'next_open', initial_nav: float = 100000.0, rebalance_every: int = 1, periods_per_year: int = 252, adv_usd: Mapping[str, float] | None = None, daily_vol: Mapping[str, float] | None = None, min_weight_change: float = 0.0) -> BacktestResult
Run weights (dates x tickers, a row of all-NaN = no decision that day, NaN cell = 0) over prices (closes).
rebalance_every=k acts on every k-th decision row. With fill="next_open", opens is required; a name without an open
that day fills at the previous close. A name without a usable close is never sized (its units are left alone).
adv_usd and daily_vol feed a cost model that needs them (square-root impact). min_weight_change is the live
path's deadband (portfolio.target_weights.rebalance_deltas): a smaller change is not traded, but a zero target always
closes the name.
lean_statistics ¶
lean_statistics(performance: Sequence[float], equity_dates: Iterable[date], *, trading_days_per_year: int, rate_changes: Sequence[tuple[date, float]]) -> dict[str, Any]
Sharpe, Sortino, PSR and their inputs exactly as Lean computes them; non-finite inputs give 0 like Lean's guards.
run_monte_carlo ¶
run_monte_carlo(per_date_net_returns: ArrayLike, *, n_runs: int = 1000, block_size: int = 20, seed: int = 42, method: str = 'block', n_plot_points: int = 500, periods_per_year: float = TRADING_DAYS_PER_YEAR) -> dict[str, Any]
Runs the bootstrap and returns a self-describing result dict:
{"config": {...},
"status": "OK" | "SKIPPED:
Equity curves are compounded from resampled daily returns and start at 1.0. The average curve is the element-wise MEAN over all runs (the red line), NOT the mean of final values.
simulate_rank_book ¶
simulate_rank_book(frame: DataFrame, **kwargs: Any) -> RankBookSimulationResult
Public entry point - see _simulate_rank_book_core()'s docstring for the full algorithm. Discards held_days_by_ticker (only capacity_curve() needs it).
simulate_target_weights ¶
simulate_target_weights(weights_by_date: Mapping[str, Mapping[str, float]], panel: MarketPanel, config: TargetWeightsConfig, *, start: str, end: str, nav: float = 100000.0, sweep_config: dict[str, Any] | None = None, sweep_ticker: str = 'BIL', rf_dates: list[str] | None = None, rf_rates: list[float] | None = None, carry_fn: Callable[[str, str], float] | None = None, max_total_drawdown: float | None = None, max_drawdown_velocity: float | None = None, fee_fn: Callable[[str, float, str, TargetWeightsConfig], float] = default_fee_usd, lot_size: Mapping[str, float] | None = None) -> dict[str, Any]
One path over [start, end]. Returns {"nav", "strategy" (ex-sweep daily return), "strategy_excess" (that, less the risk-free return on the net capital invested), "total" (daily return), "gross", "fees", "financing", "turnover", "fills", "locked_on"}, the series indexed by calendar date.
"strategy" is what Lean reports. Ex-sweep, a long book pays nothing for the cash it ties up, which the sweep would have earned, so a long sleeve's Sharpe is raw and not excess unless it is scored on "strategy_excess". A forex position is self-financed and has no such cost.
A drawdown lock (on the strategy, ex sweep, as main.py reads it) closes everything at the next fills and stops trading.
summary_statistics ¶
summary_statistics(returns: Series, *, periods_per_year: int = 252, risk_free: float = 0.0, turnover: Series | None = None, costs: Series | None = None, nav: Series | None = None) -> dict[str, float]
CAGR, volatility, Sharpe, Sortino, PSR (Lean's formulas, at a constant annual risk_free), max drawdown, Calmar,
annual turnover and annual cost drag in bps. Non-finite returns are dropped; an empty series gives zeros.