DB - backtest_runs

abstract

Stores one backtest execution per row — configuration, status, progress, all performance metrics (win rate, Sharpe, drawdown), and a link to its generated OHLCV data file.

Table Info

PropertyValue
Table Namebacktest_runs
SQLAlchemy Modelbackend/db/models.py :: BacktestRun
Pydantic Schemabackend/api/routes/backtest.py
Migration Filealembic/versions/
TimescaleDB HypertableNo
Partition Column

Columns

ColumnSQLAlchemy TypeNullableDefaultDescription
idIntegerNoautoPrimary key
strategy_idInteger FKNoFK → strategies.id (CASCADE delete, indexed)
symbolString(20)NoSymbol tested (indexed)
timeframeString(10)NoPrimary timeframe (e.g., M15)
start_dateDateTime(timezone=True)NoBacktest start date
end_dateDateTime(timezone=True)NoBacktest end date
initial_balanceFloatNo10000.0Starting capital in account currency
spread_pipsFloatNo1.5Simulated spread in pips
execution_modeString(20)No"close_price"close_price — execution at candle close
primary_tfString(10)No"M15"Primary candle timeframe
context_tfsTextNo"[]"JSON list of context timeframes
max_llm_callsIntegerNo100LLM call budget for this run
statusString(20)No"pending"pending | running | completed | failed | cancelled
progress_pctIntegerNo0Progress 0–100
error_messageTextYesnullError if status=failed
total_tradesIntegerYesnullTotal trades executed in backtest
win_rateFloatYesnullWin rate 0.0–1.0
profit_factorFloatYesnullGross profit / gross loss
expectancyFloatYesnullExpected P&L per trade
max_drawdown_pctFloatYesnullMax drawdown as % of peak equity
recovery_factorFloatYesnullNet profit / max drawdown
sharpe_ratioFloatYesnullRisk-adjusted return (annualized)
sortino_ratioFloatYesnullDownside risk-adjusted return
total_return_pctFloatYesnullTotal return %
avg_winFloatYesnullAverage winning trade P&L
avg_lossFloatYesnullAverage losing trade P&L
max_consec_winsIntegerYesnullMaximum consecutive wins
max_consec_lossesIntegerYesnullMaximum consecutive losses
avg_spreadFloatYesnullAverage spread over test period
commission_per_lotFloatNo0.0Commission cost per lot
tp_partial_close_ratioFloatNo0.5Partial close ratio at TP
data_file_pathTextYesnullPath to OHLCV CSV for chart replay
created_atDateTime(timezone=True)Nodatetime.now(UTC)Run creation timestamp (indexed)

Constraints & Indexes

NameTypeColumnsPurpose
pk_backtest_runsPRIMARY KEYidRow uniqueness
idx_backtest_strategyINDEXstrategy_idFilter by strategy
idx_backtest_symbolINDEXsymbolFilter by symbol
idx_backtest_createdINDEXcreated_atSort by recency
fk_backtest_strategyFOREIGN KEYstrategy_id → strategies.id CASCADEDelete runs when strategy deleted

Entity Relationships

SQLAlchemy Model (reference snapshot)

class BacktestRun(Base):
    __tablename__ = "backtest_runs"
 
    id: Mapped[int] = mapped_column(Integer, primary_key=True)
    strategy_id: Mapped[int] = mapped_column(Integer, ForeignKey("strategies.id", ondelete="CASCADE"), index=True)
    symbol: Mapped[str] = mapped_column(String(20), index=True)
    timeframe: Mapped[str] = mapped_column(String(10))
    start_date: Mapped[datetime] = mapped_column(DateTime(timezone=True))
    end_date: Mapped[datetime] = mapped_column(DateTime(timezone=True))
    initial_balance: Mapped[float] = mapped_column(Float, default=10000.0)
    spread_pips: Mapped[float] = mapped_column(Float, default=1.5)
    execution_mode: Mapped[str] = mapped_column(String(20), default="close_price")
    primary_tf: Mapped[str] = mapped_column(String(10), default="M15")
    context_tfs: Mapped[str] = mapped_column(Text, default="[]")
    max_llm_calls: Mapped[int] = mapped_column(Integer, default=100)
    status: Mapped[str] = mapped_column(String(20), default="pending")
    progress_pct: Mapped[int] = mapped_column(Integer, default=0)
    error_message: Mapped[str | None] = mapped_column(Text, nullable=True)
    total_trades: Mapped[int | None] = mapped_column(Integer, nullable=True)
    win_rate: Mapped[float | None] = mapped_column(Float, nullable=True)
    sharpe_ratio: Mapped[float | None] = mapped_column(Float, nullable=True)
    # ... (all metric columns)
    data_file_path: Mapped[str | None] = mapped_column(Text, nullable=True)
    created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=lambda: datetime.now(UTC), index=True)
    trades: Mapped[list["BacktestTrade"]] = relationship("BacktestTrade", back_populates="run", cascade="all, delete-orphan")
RoleLink
API EndpointAPI-POST-v1-Backtest
Frontend PagePage - Backtest
Related TableDB - strategies
Related Table[DB - backtest_trades](DB - backtest_trades)
Related TableDB - optimization_runs