Skip to content

Errors

The library raises three separate families of exception, and it helps to know which is which before catching anything. Errors about the request to UBI, such as a refused token, a rejected order or a UBI that cannot be reached, come from tradingmachine.unified_broker_interface.exceptions and are chosen by the HTTP status code UBI answered with. Errors about an instrument itself, such as an unknown symbol, an index used as something tradeable, a share used as a futures contract, or a position that cannot be changed as asked, come from tradingmachine.assets.exceptions. Errors about an asset basket, such as a basket that is not stored or a CSV file without a symbol column, come from tradingmachine.asset_baskets.exceptions.

The three families do not share a base class, so except UnifiedBrokerInterfaceError never catches an EquityError or a BasketNotFoundError, and the other way round. Where one causes the other, the instrument error is raised from the UBI error, so the original stays in the traceback as __cause__.

The table below lists every class on this page.

Kind Member Description
class UnifiedBrokerInterfaceError The base of every failure reported by, or on the way to, UBI
class Twelve status classes One per HTTP status UBI returns, plus a catch-all and one for no answer at all
class InstrumentError The base of every problem with an instrument
class Four behaviour errors Tradeable, non-tradeable, position and holding
class Six derivative errors A contract of the wrong kind given to a derivative base class, or one whose underlying cannot be found
class Twenty-seven family errors One per named instrument class, raised when UBI has no such instrument
class AssetBasketError and three basket errors A basket that is not stored, has unusable members, or cannot be imported from a CSV file

The three hierarchies

The class diagram below shows the UBI client errors. Every one of them is a direct subclass of UnifiedBrokerInterfaceError, which is itself a plain Exception.

classDiagram
    direction LR
    Exception <|-- UnifiedBrokerInterfaceError
    UnifiedBrokerInterfaceError <|-- BadRequestError
    UnifiedBrokerInterfaceError <|-- AuthenticationError
    UnifiedBrokerInterfaceError <|-- LossLockoutError
    UnifiedBrokerInterfaceError <|-- NotFoundError
    UnifiedBrokerInterfaceError <|-- ConflictError
    UnifiedBrokerInterfaceError <|-- OrderRejectedError
    UnifiedBrokerInterfaceError <|-- RateLimitError
    UnifiedBrokerInterfaceError <|-- BrokerError
    UnifiedBrokerInterfaceError <|-- ServiceUnavailableError
    UnifiedBrokerInterfaceError <|-- OrderOutcomeUnknownError
    UnifiedBrokerInterfaceError <|-- ServerError
    UnifiedBrokerInterfaceError <|-- UnreachableError
    class UnifiedBrokerInterfaceError {
        +str message
        +int status_code
        +dict detail
    }

The class diagram below shows the instrument errors. They are all direct subclasses of InstrumentError, which is also a plain Exception; the twenty-seven family errors and the six derivative errors are grouped here to keep the diagram readable, and each is listed by name further down.

classDiagram
    direction LR
    Exception <|-- InstrumentError
    InstrumentError <|-- TradeableInstrumentError
    InstrumentError <|-- NonTradeableInstrumentError
    InstrumentError <|-- PositionError
    InstrumentError <|-- HoldingError
    InstrumentError <|-- DerivativeErrors
    InstrumentError <|-- EquityFamilyErrors
    InstrumentError <|-- FixedIncomeFamilyErrors
    InstrumentError <|-- CommodityFamilyErrors
    InstrumentError <|-- CurrencyFamilyErrors
    InstrumentError <|-- FundErrors
    class EquityFamilyErrors {
        EquityError
        EquityFuturesError
        EquityOptionError
        EquityIndexError
        EquityIndexFuturesError
        EquityIndexOptionError
    }
    class FixedIncomeFamilyErrors {
        FixedIncomeError
        FixedIncomeFuturesError
        FixedIncomeOptionError
        FixedIncomeIndexError
        FixedIncomeIndexFuturesError
        FixedIncomeIndexOptionError
    }
    class CommodityFamilyErrors {
        CommodityError
        CommodityFuturesError
        CommodityOptionError
        CommodityIndexError
        CommodityIndexFuturesError
        CommodityIndexOptionError
    }
    class CurrencyFamilyErrors {
        CurrencyError
        CurrencyFuturesError
        CurrencyOptionError
        CurrencyIndexError
        CurrencyIndexFuturesError
        CurrencyIndexOptionError
    }
    class DerivativeErrors {
        DerivativeError
        FuturesError
        OptionError
        IndexFuturesError
        IndexOptionError
        UnderlyingError
    }
    class FundErrors {
        ExchangeTradedFundError
        InvestmentTrustError
        MutualFundError
    }

The six grouping boxes in that diagram are not classes; each family error and each derivative error inherits from InstrumentError directly. The third hierarchy, the asset basket errors, is drawn in its own section.

Which status becomes which exception

UBI reports a failure as an HTTP status and a JSON body, with no error-type field, so the client chooses the class from the status code alone. The table below is that mapping, which lives in EXCEPTION_FOR_STATUS_CODE.

Status Exception What UBI means by it
400 BadRequestError A parameter is missing or invalid, and the message names it
401 AuthenticationError The key or secret is wrong, or the token is missing, invalid or expired
403 LossLockoutError The day's loss is past UBI's daily loss limit
404 NotFoundError The instrument, profile or order does not exist
409 ConflictError The request conflicts with the account's state
422 OrderRejectedError The broker refused the order
429 RateLimitError The broker is at its order rate limit or daily order cap
502 BrokerError No broker's data could be read
503 ServiceUnavailableError Data is stale or not kept, no broker can take the order, a price reference cannot be resolved, the order engine is not running, or a broker's order rate budget is full
504 OrderOutcomeUnknownError The order was sent but its outcome is unknown
500, 405 and any other ServerError A failure with no more specific class
none UnreachableError No answer arrived at all

A 2xx is never raised. That includes UBI's 202 for a held limit order or a synthetic order that is armed or scheduled; its 207 for a type that sends several orders when only some were accepted, whose outcome is partial; its 207 for a parent whose cancel left one leg possibly live, whose state is cancelling; and its 207 for a flatten that partly failed, which Account.flatten returns for you to read.

The exception's message is the body's error field, or its status_message when there is no error, or else UBI returned HTTP <status>. For a 422 and a 504 UBI answers with the order document rather than an error body, so the order is in detail. Errors and status codes on the UBI site lists every message UBI can send.

What to do next

The flowchart below is a quick way to decide what to do when a call fails. The class sections after it give the detail.

flowchart LR
    A["A call raised"] --> B{"Which family?"}
    B -- "InstrumentError" --> C{"Which one?"}
    C -- "a family error" --> C1["Check the symbol, expiry, strike<br/>with search, expiries or chain"]
    C -- "PositionError" --> C2["Name the product, or check<br/>net_positions"]
    C -- "HoldingError" --> C3["Check holdings and the<br/>pledged quantity"]
    C -- "Tradeable or NonTradeable" --> C4["Use the other class"]
    C -- "a derivative error" --> C5["Use the base class<br/>for that kind of contract"]
    C -- "UnderlyingError" --> C6["Give the underlying<br/>when building the contract"]
    B -- "UnifiedBrokerInterfaceError" --> D{"Which one?"}
    D -- "BadRequestError" --> D1["Fix the call, the<br/>message names the field"]
    D -- "OrderOutcomeUnknownError" --> D2["Read orders before<br/>sending anything again"]
    D -- "LossLockoutError or RateLimitError" --> D3["Stop placing new<br/>orders today"]
    D -- "OrderRejectedError" --> D4["Read status_message<br/>in detail"]
    D -- "ConflictError" --> D5["Read orders or positions,<br/>the state has moved on"]
    D -- "ServiceUnavailableError or BrokerError" --> D6["Start the UBI service<br/>the message names"]
    D -- "UnreachableError" --> D7["Start UBI, check<br/>TRADINGMACHINE_UBI_BASE_URL"]
    D -- "AuthenticationError" --> D9["Make the MongoDB settings<br/>match UBI's key and secret"]
    B -- "AssetBasketError" --> E{"Which one?"}
    E -- "BasketNotFoundError" --> E1["Check the name and date<br/>with BasketStore.names or history"]
    E -- "BasketMemberError" --> E2["Fix the member rows,<br/>the message names them"]
    E -- "BasketCsvImportError" --> E3["Fix the CSV file's<br/>columns or rows"]

Never resend an order after OrderOutcomeUnknownError

The order may well have reached the broker. Read orders, or read the engine's answer with Account.intent and the intent_id in the exception's detail, before sending anything again, or you may end up holding twice what you meant to.

The UBI client errors

These classes live in tradingmachine.unified_broker_interface.exceptions. Catch the base class to handle every failure the same way, or one subclass to handle one case.

UnifiedBrokerInterfaceError

UnifiedBrokerInterfaceError is the base of every failure reported by, or on the way to, UBI. It carries three attributes, listed in the table below, so a handler can always read detail.get(...) without first checking for None.

Attribute Type Description
message str What went wrong, from the body's error or status_message, or a generic text naming the status
status_code int or None The HTTP status, or None when no answer arrived
detail dict The parsed JSON body, or an empty dict when there was none

The example below catches one subclass and reads all three. It is built from the code, and its output was not captured.

from tradingmachine.unified_broker_interface import exceptions

try:
    reliance.place_order("buy", "limit", 1, "cnc")
except exceptions.BadRequestError as error:
    print(error.status_code, error.message)
    print(error.detail)

BadRequestError

BadRequestError means HTTP 400: UBI could not use a parameter, such as a missing identity field, an unknown exchange, an invalid interval, or price fields that do not fit the order type. The message names the field. Fix the call rather than retrying it. A 400 is never turned into an instrument error, because it is a malformed call rather than a missing instrument.

AuthenticationError

AuthenticationError means HTTP 401. The client already retried once with a fresh token before raising it, so seeing it means the key or secret is wrong: the settings document in this project's MongoDB does not match UBI's own. Configuration shows the document.

LossLockoutError

LossLockoutError means HTTP 403, which UBI's order engine answers only when the day's loss is past its daily loss limit. Every new order is refused until the next trading day, so do not retry. The body carries the order's intent_id.

NotFoundError

NotFoundError means HTTP 404: the instrument, profile or order does not exist, or no broker has a mapping for the instrument an order names. An instrument constructor never lets it escape, because it turns a 404 on lookup into InstrumentError or the class's own family error.

ConflictError

ConflictError means HTTP 409: the request conflicts with the account's state. The common causes are listed below.

  • Modifying or cancelling an order that is no longer open, or cancelling a parent that has already finished.
  • A reduce_position or liquidate_position naming a product that is not held.
  • A reduce-only order, one sent with reduce_only=True, whose leg would not reduce the net position when it was about to be sent. Nothing was sent.
  • An order the engine read too late to place, or one whose intent had already started a parent before an engine restart, in which case the detail carries the parent_id to read.
  • Changing a leg of a synthetic order in a field other than its price, trigger price or quantity, or changing a held order that has already been sent.

Read orders or net_positions to see what changed.

OrderRejectedError

OrderRejectedError means HTTP 422: the broker answered and refused the order. UBI sends the order document instead of an error body, so the broker's reason is in detail["status_message"].

RateLimitError

RateLimitError means HTTP 429: the broker UBI chose is at its order rate limit, or has used its daily order cap. The position-closing methods mark their orders as exits, so they can use the part of the cap UBI keeps for closing positions.

BrokerError

BrokerError means HTTP 502: no broker's data could be read for the request, such as positions or the order book. It usually means UBI's background scripts for the brokers are not running.

ServiceUnavailableError

ServiceUnavailableError means HTTP 503. It covers several causes, listed below, and the message says which.

  • UBI has no recent quote and no broker that serves quotes carries the instrument, which is always true of a cash bond, a fixed income index and a mutual fund.
  • A document UBI keeps, such as positions, is missing or too old to serve.
  • No broker can take the order, or the broker's order rate budget is full. The rate budget also covers modify_order and cancel_order, which then send nothing; this cause is worth retrying a moment later.
  • A price reference cannot be resolved: there is no live quote, the book is too shallow for the level asked for, or the brokers do not agree on a tick size.
  • The order engine is not running, so nothing was queued. UBI places every order through it, and it also runs cancel_parent and the changes to an order the engine placed.
  • For commodities, UBI does not trust the contract's size that day, which the body reports as contract_size_status.

OrderOutcomeUnknownError

OrderOutcomeUnknownError means HTTP 504: the order was sent, or handed to the engine, and whether it took effect is unknown. The order may exist. Read the order book before doing anything else. When UBI's order engine did not answer in time, the message is taken from status_message, such as "the order engine did not answer within 5.0 seconds, so this order may still be placed".

ServerError

ServerError is the catch-all for any failure status without a class of its own, such as HTTP 500 when UBI's own settings document is missing, or HTTP 405 from a patch, because UBI has no PATCH route.

UnreachableError

UnreachableError means no answer arrived at all: the connection was refused, the address was wrong, or the request timed out. Its status_code is None, and the original requests exception is chained as its cause. Check that UBI is running and that TRADINGMACHINE_UBI_BASE_URL points at it.

The instrument errors

These classes live in tradingmachine.assets.exceptions. Catch InstrumentError to handle every one of them, including the six derivative errors and all twenty-seven family errors, thirty-seven subclasses in all.

InstrumentError

InstrumentError is the base of every problem with an instrument itself. Instrument, TradeableInstrument and NonTradeableInstrument raise it directly when UBI answers 404 to a lookup, chained from the NotFoundError. The named classes catch it and raise their own family error instead.

TradeableInstrumentError

TradeableInstrumentError means an instrument was asked for as tradeable but is an index, whose segment ends in _indices. Only TradeableInstrument built with a lookup of your own can raise it, because every named class fixes a segment that agrees with its base class.

NonTradeableInstrumentError

NonTradeableInstrumentError means an instrument was asked for as non-tradeable but is not an index, so it can be traded. Only NonTradeableInstrument built with a lookup of your own can raise it.

PositionError

PositionError means a position cannot be changed as asked. The table below lists when each position method raises it.

Method When
add_to_position Several positions are held and no product was named, or the product named is not held, or transaction_type points the other way from the position, or nothing is held and no direction and product were given
reduce_position, liquidate_position No product was named and there is not exactly one position, or the product is not cnc, mis or nrml

For reduce_position and liquidate_position, a named product that is not held comes back from UBI as ConflictError instead, because UBI reads the position itself.

HoldingError

HoldingError means a holding cannot be changed as asked. The holdings methods on Equity, FixedIncome, ExchangeTradedFund, InvestmentTrust and MutualFund raise it when the instrument is not held, when a sale is larger than the units free to sell, and when the whole holding is pledged as collateral.

The derivative errors

These six are raised by the derivative base classes in tradingmachine.assets.instruments, documented on Derivatives. A family class such as EquityFutures never raises the first five in practice, because it names its own segment and UBI resolves that segment to exactly one kind of contract; they fire when a base class is built directly, typically from an instrument_id of unknown kind. Like every other instrument error they are direct subclasses of InstrumentError, so FuturesError does not inherit from DerivativeError.

The table below lists what each one means.

Error Raised when
DerivativeError Any derivative base class is given something that is neither a future nor an option, a contract with no expiry date, or one in a segment with no known underlying segment. It is checked first, so a share given to Futures raises this rather than FuturesError.
FuturesError Futures or IndexFutures is given an option, or expiries or contracts is called on one of those two bare classes rather than on a family class
OptionError Option or IndexOption is given a future or an option with no strike price or option type, or expiries, strikes or chain is called on one of those two bare classes
IndexFuturesError IndexFutures is given a futures contract whose segment is not an index futures segment, such as a share future
IndexOptionError IndexOption is given an option whose segment is not an index options segment, such as a share option
UnderlyingError A contract's underlying cannot be found: none was given, UBI links it to none, and its family's default finds none. Every future outside equities raises it from underlying, underlying_price and the basis members unless given an underlying, and so do the bse USDINR-CNV and USDINR-STD options, which have no future to be priced off. Derivatives gives the order a contract tries.

The family errors

Each of the twenty-seven named classes has an error of its own, raised by its constructor when UBI has no instrument matching the arguments, chained from the InstrumentError beneath it. The constructor also raises it if UBI ever returned an instrument outside the class's segment, which cannot happen today but guards against a change in UBI. The fix is almost always to find the right identity with search, expiries or chain first.

Some classes resolve nothing today because their segment is empty in UBI, so every call to their constructor raises their error: FixedIncomeIndexOption, CurrencyIndex, CurrencyIndexFutures and CurrencyIndexOption. Asset classes explains why.

EquityError

EquityError is raised by Equity when UBI has no share with that symbol on that exchange.

EquityFuturesError

EquityFuturesError is raised by EquityFutures when UBI has no share futures contract on that underlying with that expiry.

EquityOptionError

EquityOptionError is raised by EquityOption when UBI has no share option with that underlying, expiry, strike and option type.

EquityIndexError

EquityIndexError is raised by EquityIndex when UBI has no equity index with that symbol on that exchange.

EquityIndexFuturesError

EquityIndexFuturesError is raised by EquityIndexFutures when UBI has no index futures contract on that underlying with that expiry.

EquityIndexOptionError

EquityIndexOptionError is raised by EquityIndexOption when UBI has no index option with that underlying, expiry, strike and option type.

FixedIncomeError

FixedIncomeError is raised by FixedIncome when UBI has no bond with that symbol, which is usually an ISIN such as IN000126C010.

FixedIncomeFuturesError

FixedIncomeFuturesError is raised by FixedIncomeFutures when UBI has no bond futures contract on that underlying, such as 633GS2035, with that expiry.

FixedIncomeOptionError

FixedIncomeOptionError is raised by FixedIncomeOption when UBI has no bond option with that underlying, expiry, strike and option type.

FixedIncomeIndexError

FixedIncomeIndexError is raised by FixedIncomeIndex when UBI has no fixed income index with that symbol.

FixedIncomeIndexFuturesError

FixedIncomeIndexFuturesError is raised by FixedIncomeIndexFutures when UBI has no fixed income index futures contract on that underlying with that expiry.

FixedIncomeIndexOptionError

FixedIncomeIndexOptionError is raised by FixedIncomeIndexOption every time today, because no broker fills the fixed_income_index_options segment.

CommodityError

CommodityError is raised by Commodity when UBI has no commodity with that symbol on that exchange.

CommodityFuturesError

CommodityFuturesError is raised by CommodityFutures when UBI has no commodity futures contract, such as MCX GOLD, with that expiry.

CommodityOptionError

CommodityOptionError is raised by CommodityOption when UBI has no commodity option with that underlying, expiry, strike and option type.

CommodityIndexError

CommodityIndexError is raised by CommodityIndex when UBI has no commodity index, such as MCXBULLDEX, with that symbol.

CommodityIndexFuturesError

CommodityIndexFuturesError is raised by CommodityIndexFutures when UBI has no commodity index futures contract on that underlying with that expiry.

CommodityIndexOptionError

CommodityIndexOptionError is raised by CommodityIndexOption when UBI has no commodity index option with that underlying, expiry, strike and option type.

CurrencyError

CurrencyError is raised by Currency when UBI has no currency pair with that symbol on that exchange.

CurrencyFuturesError

CurrencyFuturesError is raised by CurrencyFutures when UBI has no currency futures contract, such as USDINR, with that expiry.

CurrencyOptionError

CurrencyOptionError is raised by CurrencyOption when UBI has no currency option with that underlying, expiry, strike and option type.

CurrencyIndexError

CurrencyIndexError is raised by CurrencyIndex every time today, because the currency_indices segment holds no rows on any exchange.

CurrencyIndexFuturesError

CurrencyIndexFuturesError is raised by CurrencyIndexFutures every time today, because the currency_index_futures segment holds no rows on any exchange.

CurrencyIndexOptionError

CurrencyIndexOptionError is raised by CurrencyIndexOption every time today, because the currency_index_options segment holds no rows on any exchange.

ExchangeTradedFundError

ExchangeTradedFundError is raised by ExchangeTradedFund when UBI has no exchange traded fund, such as NIFTYBEES, with that symbol on that exchange.

InvestmentTrustError

InvestmentTrustError is raised by InvestmentTrust when UBI has no investment trust, such as EMBASSY, with that symbol on that exchange.

MutualFundError

MutualFundError is raised by MutualFund when UBI has no mutual fund scheme with that code, such as ABSLFTTIDG, on that exchange.

The asset basket errors

These classes live in tradingmachine.asset_baskets.exceptions and are raised by the asset basket classes and by the constituents property. Failures reported by UBI itself during a basket's requests still arrive as the UBI client errors. The class diagram below shows the family.

classDiagram
    direction LR
    Exception <|-- AssetBasketError
    AssetBasketError <|-- BasketNotFoundError
    AssetBasketError <|-- BasketMemberError
    AssetBasketError <|-- BasketCsvImportError

Each of the three specific errors inherits from AssetBasketError, so except AssetBasketError catches all four.

AssetBasketError

AssetBasketError is the base of every failure in building, reading, storing or trading an asset basket. It is also raised directly in two cases: BasketStore finds a stored basket whose kind it does not know, and Index.level is read on an index that has no base_date.

BasketNotFoundError

BasketNotFoundError is raised by BasketStore.load when no stored basket has the name asked for, or none is in effect on the date asked for. The constituents property does not raise it, because it returns None when nothing is stored for the instrument.

BasketMemberError

BasketMemberError means a basket's members are unusable. The list below gives the common causes.

  • The members are empty, name the same instrument twice, or give a weight to only some members.
  • UBI could not find one or more of the instruments a row, a CSV file or a stored basket names, or a row gives neither an instrument_id nor an exchange and a segment.
  • UBI answered an error for a member when last prices or candles were asked for.
  • A Portfolio member has no quantity, a portfolio's weights are asked for while a member has no last price, or Portfolio.from_holdings or from_positions finds nothing held.
  • An Index with weighting="stated" has a member without a weight, Index.level finds a member with no candle near the base_date, or to_portfolio is given too little capital to buy one whole unit of any member.
  • remove_member names an instrument that is not in the basket.

BasketCsvImportError

BasketCsvImportError is raised by BasketCsvImporter.import_file when a CSV file cannot be turned into a basket: it has no symbol column, it has no rows, or only some of its rows give a number in a column such as weight or quantity.

Errors that are not the library's own

A few failures come from Python or from the configuration rather than from either family. The table below lists them.

Exception Raised by When
TypeError Any named constructor A required identity argument is missing, reported before any request
ValueError UnifiedBrokerInterface() and so the first instrument No base url, or no UBI settings document or key or secret in MongoDB
ValueError The discovery calls and the constructors A date string that is not YYYY-MM-DD
ValueError The basket constructors and BasketStore An unmapped_weight outside 0 to 1, an unknown weighting or a base value that is not positive for Index, or no MongoDB database name configured
pymongo.errors.PyMongoError BasketStore, BasketCsvImporter and constituents MongoDB could not be reached
AttributeError An index class Reading an order-book or order member, which an index does not have
Under the hood

The status mapping is EXCEPTION_FOR_STATUS_CODE in src/tradingmachine/unified_broker_interface/exceptions.py, and the choice is made by the client's _raise_for_failure. The reasoning behind each class, including why the 403 class is named for the loss lockout rather than called ForbiddenError, is in .claude/notes/src/tradingmachine/unified_broker_interface/exceptions.py.md.