Skip to content

trading_agent.risk.trading_limits

Reads the hard limits from the fenced ```toml limits block of TradingRules.md.

The file is read before every order, so an accepted change to the rules takes effect on the next order. A file without the block, or with a block that is not valid, makes the risk engine refuse every order that is not closing a position: when the limits cannot be known, the safe answer is no.

Typical usage example:

limits = TradingLimitsReader(Path('workspace/shared/TradingRules.md')).read() limits.account.daily_loss_limit_percent

LimitsError

Bases: ValueError

TradingRules.md has no readable limits block.

AccountLimits

Bases: BaseModel

The limits for the whole account, shared by every desk.

Attributes:

Name Type Description
trading_enabled bool

Whether any desk may open or enlarge a position.

capital_percent float

The share of the total balance the agent may commit as margin.

daily_loss_limit_percent float

Today's loss, as a share of the live total balance, that stops every desk and closes every position.

weekly_loss_limit_percent float

This week's loss, as a share of the live total balance, that stops every desk until Monday.

maximum_order_value_percent float

The largest margin one order may block, as a share of the live total balance.

maximum_open_positions int | None

The most instruments that may be held at once, or None for no cap.

trading_window str

"exchange_hours" for each exchange's own session, or "HH:MM-HH:MM" in India time for every exchange.

allow_option_writing bool

Whether a desk may sell options to open or enlarge a short position.

DeskLimits

Bases: BaseModel

The limits for one desk, on top of the account's.

Attributes:

Name Type Description
capital_share_percent float | None

The desk's share of the agent's capital, or None to draw on the shared pool.

allowed_products list[str]

The products the desk may use, such as mis, cnc or nrml; empty allows all.

allowed_instruments list[str]

The symbols the desk may trade; empty allows all.

maximum_orders_per_minute int | None

How many orders the desk may send in a minute, or None for no cap.

daily_loss_limit_percent float | None

Today's loss on the instruments this desk owned, as a share of the live total balance, that stops this desk alone for the rest of the day; None for no desk limit.

TradingLimits

Bases: BaseModel

Everything in the limits block.

Attributes:

Name Type Description
account AccountLimits

The account-wide limits.

desks dict[str, DeskLimits]

The per-desk limits, keyed by desk name; a desk without an entry has no extra limits.

desk

desk(desk_name)

The limits of one desk.

Parameters:

Name Type Description Default
desk_name str

The desk.

required

Returns:

Type Description
DeskLimits

Its limits, or limits that add nothing when the desk has no entry.

Source code in src/trading_agent/risk/trading_limits.py
def desk(self, desk_name: str) -> DeskLimits:
    """The limits of one desk.

    Args:
        desk_name (str): The desk.

    Returns:
        DeskLimits: Its limits, or limits that add nothing when the desk has no entry.
    """
    return self.desks.get(desk_name, DeskLimits())

TradingLimitsReader

TradingLimitsReader(rules_file)

Reads the limits block from one TradingRules.md.

Remembers which file to read.

Parameters:

Name Type Description Default
rules_file Path

The TradingRules.md file.

required
Source code in src/trading_agent/risk/trading_limits.py
def __init__(self, rules_file: Path):
    """Remembers which file to read.

    Args:
        rules_file (Path): The TradingRules.md file.
    """
    self._rules_file = rules_file

read

read()

Reads and checks the limits block.

Returns:

Type Description
TradingLimits

The limits.

Raises:

Type Description
LimitsError

The file is missing, has no limits block, or the block is not valid TOML with valid values.

Source code in src/trading_agent/risk/trading_limits.py
def read(self) -> TradingLimits:
    """Reads and checks the limits block.

    Returns:
        TradingLimits: The limits.

    Raises:
        LimitsError: The file is missing, has no limits block, or the block is not valid TOML with valid values.
    """
    try:
        text = self._rules_file.read_text(encoding='utf-8')
    except FileNotFoundError as error:
        raise LimitsError(f'{self._rules_file} does not exist') from error
    match = _LIMITS_BLOCK.search(text)
    if match is None:
        raise LimitsError(
            f'{self._rules_file.name} has no ```toml limits block'
        )
    try:
        parsed = tomllib.loads(match.group(1))
        return TradingLimits.model_validate(parsed)
    except (tomllib.TOMLDecodeError, pydantic.ValidationError) as error:
        raise LimitsError(
            f'The limits block in {self._rules_file.name} is not valid: {error}'
        ) from error