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.
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_positionorliquidate_positionnaming 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
detailcarries theparent_idto 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_orderandcancel_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_parentand 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_idnor anexchangeand asegment. - UBI answered an error for a member when last prices or candles were asked for.
- A
Portfoliomember has no quantity, a portfolio's weights are asked for while a member has no last price, orPortfolio.from_holdingsorfrom_positionsfinds nothing held. - An
Indexwithweighting="stated"has a member without a weight,Index.levelfinds a member with no candle near thebase_date, orto_portfoliois given too little capital to buy one whole unit of any member. remove_membernames 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.