Skip to content

Database API Reference

Models

tradingbot.utils.db.Bot

Bases: Base

Bot model representing a trading bot instance.

Attributes:

Name Type Description
name Mapped[str]

Unique bot name (primary key)

description Mapped[str | None]

Optional description of the bot

portfolio Mapped[dict]

JSON dictionary representing portfolio holdings (default: {"USD": 10000}) Format: {"USD": cash_amount, "SYMBOL": quantity, ...}

created_at Mapped[datetime | None]

Timestamp when bot was created

updated_at Mapped[datetime | None]

Timestamp when bot was last updated

tradingbot.utils.db.Trade

Bases: Base

Trade model representing a single trade execution.

Attributes:

Name Type Description
id Mapped[int]

Auto-incrementing trade ID (primary key)

bot_name Mapped[str | None]

Name of the bot that executed the trade (foreign key to Bot.name)

symbol Mapped[str | None]

Trading symbol (e.g., "QQQ", "EURUSD=X")

isBuy Mapped[bool | None]

True for buy orders, False for sell orders

quantity Mapped[float | None]

Number of shares/units traded

price Mapped[float | None]

Price per unit at time of trade

timestamp Mapped[datetime | None]

Timestamp when trade was executed

profit Mapped[float | None]

MISNOMER — this is the net cash proceeds credited on a sell, NOT realized P&L. No cost basis is tracked anywhere, so summing this column does not give profit. NULL on buys.

tradingbot.utils.db.HistoricData

Bases: Base

Historic market data model for storing OHLCV data.

interval is part of the primary key, and must stay that way. Without it the key is (symbol, timestamp), which lets a symbol's 1-minute and daily bars collide in one pile: xauzenbot writes ^XAU at 1m every 5 minutes, so a later request for interval="1d" silently returned 74k one-minute rows and every TA indicator computed on top of them was meaningless. Reads MUST filter on interval, and writes MUST set it.

Attributes:

Name Type Description
symbol Mapped[str]

Trading symbol (primary key, part of composite key)

interval Mapped[str]

Bar size the row was fetched at, e.g. "1m", "1d", "1wk" (primary key, part of composite key)

timestamp Mapped[datetime]

Timestamp of the data point (primary key, part of composite key)

open Mapped[float | None]

Opening price

high Mapped[float | None]

Highest price

low Mapped[float | None]

Lowest price

close Mapped[float | None]

Closing price

volume Mapped[float | None]

Trading volume

tradingbot.utils.db.RunLog

Bases: Base

Run log model for tracking bot execution history.

Attributes:

Name Type Description
id Mapped[int]

Auto-incrementing log ID (primary key)

bot_name Mapped[str | None]

Name of the bot (foreign key to Bot.name)

start_time Mapped[datetime | None]

Timestamp when the run started

success Mapped[bool | None]

Whether the run completed successfully

result Mapped[str | None]

Result message (nullable, contains decision/error info)

tradingbot.utils.db.PortfolioWorth

Bases: Base

Portfolio worth model for tracking portfolio value over time.

Attributes:

Name Type Description
bot_name Mapped[str]

Name of the bot (primary key, part of composite key, foreign key to Bot.name)

date Mapped[datetime]

Date of the portfolio valuation (primary key, part of composite key)

portfolio_worth Mapped[float]

Total portfolio value in USD

holdings Mapped[dict]

JSON dictionary of holdings at this date

created_at Mapped[datetime | None]

Timestamp when this record was created

Session Management

tradingbot.utils.db.get_db_session() -> Generator[Session, None, None]

Simple context manager for database sessions.

Ensures proper session cleanup and rollback on exceptions. NOTE: We intentionally avoid internal retry loops here because a @contextmanager generator must yield exactly once; retry logic is better handled at call sites if needed.

Usage

with get_db_session() as session: # Use session here session.query(Bot).all()

Source code in tradingbot/utils/db.py
@contextmanager
def get_db_session() -> Generator[Session, None, None]:
    """
    Simple context manager for database sessions.

    Ensures proper session cleanup and rollback on exceptions.
    NOTE: We intentionally avoid internal retry loops here because a
    @contextmanager generator must yield exactly once; retry logic is
    better handled at call sites if needed.

    Usage:
        with get_db_session() as session:
            # Use session here
            session.query(Bot).all()
    """
    session: Session | None = None
    try:
        session = SessionLocal()
        yield session
        session.commit()
    except Exception as e:
        if session:
            with suppress(Exception):
                session.rollback()
        logger.error(f"Unexpected error in database session: {type(e).__name__}: {e}")
        raise
    finally:
        if session:
            with suppress(Exception):
                session.close()