Skip to content

Instruments

An instrument is one thing you can look up at UBI, such as a share, an index, a futures contract or an option, held as a Python object. You build it once by naming it, it looks itself up in UBI with one request, and from then on it carries its identity, its lot size and tick size, and every member described on the other pages of this tab.

The table below lists what this page covers. The twenty-seven named classes are the ones you normally use; the eight base classes underneath them are documented at the end.

Kind Member Description
class Named by exchange and symbol The eleven classes for shares, indices, bonds, currency pairs, commodities, funds, trusts and mutual funds
class Named by underlying and expiry The eight futures classes
class Named by underlying, expiry, strike and option type The eight option classes
attribute The attributes set on lookup instrument_id, exchange, segment, shape, symbol and the rest, fixed at construction
classmethod shared_unified_broker_interface The one UBI client every instrument shares
property constituents The stored basket of what an index, an exchange traded fund or a mutual fund holds
class Instrument The base of every instrument, which quotes and analyses but cannot trade
class TradeableInstrument An instrument that can be traded, which is anything except an index
class NonTradeableInstrument An index, which is followed rather than traded
class Derivative, Futures, Option, IndexFutures, IndexOption The bases every futures and option class inherits, whose members are on Derivatives

The strings these constructors accept, such as nse, CE and the segment names, are listed on Vocabulary.

The twenty-seven classes

Each class stands for one of UBI's segments, so the kind of contract is the class you pick rather than a segment string you type. The diagram below shows how the classes are built: every one of them inherits from Instrument through one of two middle classes, only the four index classes are non-tradeable, and the sixteen futures and option classes inherit a derivative base in between.

flowchart TB
    I["Instrument<br/>identity, candles, quotes,<br/>about 210 analysis methods"]
    T["TradeableInstrument<br/>order book, orders,<br/>positions"]
    N["NonTradeableInstrument<br/>indices only"]
    I --> T
    I --> N
    T --> C1["Equity, FixedIncome,<br/>Commodity, Currency"]
    T --> C2["ExchangeTradedFund,<br/>InvestmentTrust, MutualFund"]
    T --> D["Derivative<br/>expiry, underlying"]
    D --> FB["Futures<br/>basis"]
    D --> OB["Option<br/>moneyness, greeks"]
    FB --> IF["IndexFutures"]
    OB --> IO["IndexOption"]
    FB --> F["four futures classes"]
    IF --> F2["four index futures classes"]
    OB --> O["four option classes"]
    IO --> O2["four index option classes"]
    N --> X["EquityIndex, FixedIncomeIndex,<br/>CommodityIndex, CurrencyIndex"]

The table below lists every class with its module, the segment it fixes, and the constructor arguments that name one of its instruments. The unified_broker_interface argument, which every class also takes, is left out of the last column.

Class Module Segment Base Named by
Equity equities equities Tradeable exchange, symbol
EquityFutures equities equity_futures Futures exchange, underlying_symbol, expiry_date
EquityOption equities equity_options Option exchange, underlying_symbol, expiry_date, strike_price, option_type
EquityIndex equities equity_indices Non-tradeable exchange, symbol
EquityIndexFutures equities equity_index_futures IndexFutures exchange, underlying_symbol, expiry_date
EquityIndexOption equities equity_index_options IndexOption exchange, underlying_symbol, expiry_date, strike_price, option_type
FixedIncome fixed_income fixed_income Tradeable exchange, symbol
FixedIncomeFutures fixed_income fixed_income_futures Futures exchange, underlying_symbol, expiry_date
FixedIncomeOption fixed_income fixed_income_options Option exchange, underlying_symbol, expiry_date, strike_price, option_type
FixedIncomeIndex fixed_income fixed_income_indices Non-tradeable exchange, symbol
FixedIncomeIndexFutures fixed_income fixed_income_index_futures IndexFutures exchange, underlying_symbol, expiry_date
FixedIncomeIndexOption fixed_income fixed_income_index_options IndexOption exchange, underlying_symbol, expiry_date, strike_price, option_type
Commodity commodities commodities Tradeable exchange, symbol
CommodityFutures commodities commodity_futures Futures exchange, underlying_symbol, expiry_date
CommodityOption commodities commodity_options Option exchange, underlying_symbol, expiry_date, strike_price, option_type
CommodityIndex commodities commodity_indices Non-tradeable exchange, symbol
CommodityIndexFutures commodities commodity_index_futures IndexFutures exchange, underlying_symbol, expiry_date
CommodityIndexOption commodities commodity_index_options IndexOption exchange, underlying_symbol, expiry_date, strike_price, option_type
Currency currencies currencies Tradeable exchange, symbol
CurrencyFutures currencies currency_futures Futures exchange, underlying_symbol, expiry_date
CurrencyOption currencies currency_options Option exchange, underlying_symbol, expiry_date, strike_price, option_type
CurrencyIndex currencies currency_indices Non-tradeable exchange, symbol
CurrencyIndexFutures currencies currency_index_futures IndexFutures exchange, underlying_symbol, expiry_date
CurrencyIndexOption currencies currency_index_options IndexOption exchange, underlying_symbol, expiry_date, strike_price, option_type
ExchangeTradedFund funds exchange_traded_funds Tradeable exchange, symbol
InvestmentTrust funds investment_trusts Tradeable exchange, symbol
MutualFund mutual_funds mutual_funds Tradeable exchange, symbol

Every module lives under tradingmachine.assets, so Equity is imported with from tradingmachine.assets import equities. Some classes exist for symmetry and resolve nothing today, because UBI holds no rows in their segment; Asset classes says which, and what each family can and cannot do.

Tradeable does not always mean orderable

Commodity is built on TradeableInstrument, but the rows in UBI's commodities segment are the exchange's underlying reference records rather than contracts, so UBI refuses an order for one and has no quote for it. The class carries the order members anyway, because the tradeable rule is only "not an index". Commodities has the details.

Named by exchange and symbol

class Equity(exchange, symbol, unified_broker_interface=None)GET /api/instruments/details

Eleven classes name their instrument by an exchange and a symbol: Equity, EquityIndex, FixedIncome, FixedIncomeIndex, Commodity, CommodityIndex, Currency, CurrencyIndex, ExchangeTradedFund, InvestmentTrust and MutualFund. Each constructor sends one request to UBI with its own segment filled in, keeps what comes back, and raises its own error class if UBI has no such instrument. The signature above is Equity's, and the other ten are identical apart from the class name.

Parameters

Name Type Required Default Description
exchange str Yes The exchange, such as nse, bse or mcx
symbol str Yes The symbol, such as RELIANCE, NIFTY, the ISIN IN000126C010 for a bond, or the scheme code ABSLFTTIDG for a mutual fund. UBI upper-cases it, so reliance works too.
unified_broker_interface UnifiedBrokerInterface or None No None The client to send requests through. None uses the shared client, which is almost always what you want.

Example

The output below was captured from a local UBI on 2026-09-26.

from tradingmachine.assets import equities

reliance = equities.Equity("nse", "RELIANCE")
print(repr(reliance))
Equity(exchange='nse', segment='nse_equities', symbol='RELIANCE')

Returns

The constructor returns the instrument object, with the attributes below already set.

Raises

Exception When
The class's own error, such as EquityError UBI has no instrument with that symbol on that exchange in this class's segment
TypeError An argument is missing, which Python reports before any request is sent
BadRequestError UBI does not know the exchange
UnifiedBrokerInterfaceError Any other failure reported by, or on the way to, UBI

Named by underlying and expiry

class EquityFutures(exchange, underlying_symbol, expiry_date, underlying=None, unified_broker_interface=None)GET /api/instruments/details

The eight futures classes name a contract by its exchange, the symbol of what it is written on, and the day it expires: EquityFutures, EquityIndexFutures, FixedIncomeFutures, FixedIncomeIndexFutures, CommodityFutures, CommodityIndexFutures, CurrencyFutures and CurrencyIndexFutures. Pass the underlying's own object as underlying to give the contract its underlying reliably, because UBI links the two only by the symbol strings matching and not every underlying carries the contract's underlying_symbol.

Parameters

Name Type Required Default Description
exchange str Yes The exchange, such as nse or mcx
underlying_symbol str Yes The symbol of the underlying, such as RELIANCE, NIFTY or GOLD
expiry_date datetime.date or str Yes The expiry, as a date or a YYYY-MM-DD string. expiries lists the valid ones.
underlying Instrument or None No None The instrument the contract is written on, such as an Equity, an EquityIndex or a future, which the contract keeps for underlying and underlying_price. None uses UBI's link to the underlying, the underlying_instrument_id attribute, when UBI gives one, and otherwise the family's default, looked up again on every read; How a contract finds its underlying gives the order.
unified_broker_interface UnifiedBrokerInterface or None No None The client to send requests through, or None for the shared one

Example

The output below was captured from a local UBI on 2026-09-26. The expiry was taken from CommodityFutures.expiries, whose first answer was 2026-10-05.

from tradingmachine.assets import commodities

expiries = commodities.CommodityFutures.expiries("mcx", "GOLD")
gold = commodities.CommodityFutures("mcx", "GOLD", expiries[0])
print(repr(gold))
print((gold.lot_size, gold.tick_size))
CommodityFutures(exchange='mcx', segment='mcx_commodity_futures', underlying_symbol='GOLD', expiry_date='2026-10-05')
(100, Decimal('1'))

Returns

The constructor returns the contract object, with the attributes below set and symbol left as None.

Raises

Exception When
The class's own error, such as EquityFuturesError UBI has no such contract, including a date that is not one of the underlying's expiries
TypeError An argument is missing
ValueError The expiry string is not a valid ISO date
UnifiedBrokerInterfaceError Any other failure reported by, or on the way to, UBI

Named by underlying, expiry, strike and option type

class EquityIndexOption(exchange, underlying_symbol, expiry_date, strike_price, option_type, underlying=None, unified_broker_interface=None)GET /api/instruments/details

The eight option classes add a strike price and an option type to the futures arguments: EquityOption, EquityIndexOption, FixedIncomeOption, FixedIncomeIndexOption, CommodityOption, CommodityIndexOption, CurrencyOption and CurrencyIndexOption. The usual way to get the four identity values right is to read them off a row of chain rather than typing them.

Parameters

Name Type Required Default Description
exchange str Yes The exchange, such as nse
underlying_symbol str Yes The symbol of the underlying, such as NIFTY
expiry_date datetime.date or str Yes The expiry, as a date or a YYYY-MM-DD string
strike_price float Yes The strike price in rupees, such as 25000
option_type str Yes CE for a call or PE for a put
underlying Instrument or None No None The instrument the contract is written on, such as an Equity, an EquityIndex or a future, which the contract keeps for underlying and underlying_price. None uses UBI's link to the underlying, the underlying_instrument_id attribute, when UBI gives one, and otherwise the family's default, looked up again on every read; How a contract finds its underlying gives the order.
unified_broker_interface UnifiedBrokerInterface or None No None The client to send requests through, or None for the shared one

Example

This example is the one in the equities module's own docstring. Its output was not captured, so none is shown.

from tradingmachine.assets import equities

option = equities.EquityIndexOption(
    exchange="nse",
    underlying_symbol="NIFTY",
    expiry_date="2026-09-29",
    strike_price=25000,
    option_type="CE",
)
premium = option.last_price

A check recorded in the project's notes on 2026-09-20 built EquityOption("nse", "RELIANCE", "2026-09-29", 1270.0, "PE") from a row of the RELIANCE chain. UBI returned the same instrument_id the row carried, 12278f86-2feb-54b0-875f-4c1311d550fc, with a lot size of 500 and a tick size of 0.05, which is the evidence that a row found by discovery always turns into the right contract.

Returns

The constructor returns the option object, with strike_price as a float and option_type as CE or PE.

Raises

Exception When
The class's own error, such as EquityIndexOptionError UBI has no such option
TypeError An argument is missing
ValueError The expiry string is not a valid ISO date
UnifiedBrokerInterfaceError Any other failure reported by, or on the way to, UBI

How an instrument is looked up

Every constructor, whatever its class, ends in the same single request to UBI's details route, unless the three base classes are handed UBI's answer already through details. The sequence below shows that request for Equity("nse", "RELIANCE"), including the two checks that run after the answer arrives.

sequenceDiagram
    autonumber
    participant You as Your code
    participant Equity as Equity constructor
    participant Client as UBI client
    participant UBI as UBI
    You->>Equity: Equity("nse", "RELIANCE")
    Equity->>Client: get /api/instruments/details<br/>exchange=nse, segment=equities, symbol=RELIANCE
    Client->>UBI: GET with the access-token header
    UBI-->>Client: 200 with the identity, lot size, tick size, carried_by
    Client-->>Equity: the parsed dict
    Equity->>Equity: keep the fields as attributes
    Equity->>Equity: check it is not an index
    Equity->>Equity: check the segment is nse_equities
    Equity-->>You: the Equity object

A 404 from UBI is turned into InstrumentError inside Instrument, and each named class catches that and raises its own error, such as EquityError, chained with from so UBI's own message stays in the traceback. After construction the object sends only its instrument_id on every request, so the identity fields are never parsed again.

What the instrument_id is

The instrument_id is a UUID that UBI computes rather than hands out. It is a UUID version 5 of the instrument's natural key, which is its exchange, its segment, its shape and its identity fields joined together, so every broker that lists the same instrument arrives at the same id, and the id stays the same from one day to the next. Naming an instrument on the UBI site explains the key in full.

This library never computes the id itself. The table below shows the two ways a constructor can send the lookup, the third way of skipping it, and which classes use each.

Lookup What is sent Used by
By identity exchange, segment and the identity fields for the segment's shape The twenty-seven named classes, always
By id instrument_id alone Instrument, TradeableInstrument, NonTradeableInstrument and the five derivative base classes when you pass instrument_id=
From details already fetched Nothing, because UBI's answer is passed in Instrument, TradeableInstrument and NonTradeableInstrument when you pass details=, which is how an asset basket builds every member from one list request

Two instrument objects compare equal when their instrument_id values are equal, and an instrument hashes by its id, so instruments work as dictionary keys and set members. An instrument built by id equals the same instrument built from its fields.

The attributes set on lookup

The constructor copies UBI's answer into plain attributes, which never change for the life of the object. The table below lists each one with its type and the value captured for Equity("nse", "RELIANCE") from a local UBI on 2026-09-26.

Attribute Type RELIANCE Description
instrument_id str '3f92570a-9924-5bf5-9f9d-e006cd9f4202' UBI's UUID for the instrument, the same at every broker
exchange str 'nse' The exchange, lower case
segment str 'nse_equities' The segment, always with the exchange as a prefix
shape str 'security' security, future or option
symbol str or None 'RELIANCE' The symbol of a security, or None for a future or an option
underlying_symbol str or None None The underlying of a future or an option
expiry_date datetime.date or None None The expiry of a future or an option
strike_price float or None None The strike price of an option
option_type str or None None CE or PE for an option
underlying_instrument_id str or None None UBI's id for the instrument a future or an option is written on, resolved from the brokers' own records, or None for a security or when UBI does not say
mapping_date datetime.date datetime.date(2026, 9, 26) The day of the UBI mapping the details were read from
first_seen_date datetime.date or None datetime.date(2026, 8, 7) The first day UBI saw the instrument
last_seen_date datetime.date or None datetime.date(2026, 9, 26) The last day UBI saw the instrument
lot_size int or None 1 Underlying units in one lot, or None when the brokers tie
tick_size decimal.Decimal or None Decimal('0.1') The smallest price step in rupees, kept exact, or None when the brokers tie
carried_by list[dict] nine entries, below One entry per broker that carries the instrument, with that broker's own figures

carried_by keeps each broker's own spelling of the instrument, exactly as UBI stores it, with the numbers as strings. The table below is RELIANCE's full list from the same capture.

broker broker_token order_symbol lot_size tick_size
dhan 2885 None '1.0' '0.1'
flattrade 2885 'RELIANCE-EQ' '1.0' None
fyers 10100000002885 'NSE:RELIANCE-EQ' '1.0' '0.1'
groww 2885 'RELIANCE' '1.0' '0.1'
indmoney 2885 None '1.0' '0.1'
kotak 2885 'RELIANCE-EQ' '1.0' '0.1'
shoonya 2885 'RELIANCE-EQ' '1.0' '0.1'
stoxkart 2885 None '1.0' '0.1'
zerodha 738561 'RELIANCE' '1.0' '0.1'

Use lot_size and tick_size, not a carried_by entry

The brokers do not always agree on units, and on MCX one broker's lot is another's single unit. The top-level lot_size and tick_size are the values UBI decided on for the instrument itself. For currencies even lot_size is not the lot an order is measured against; Currencies explains why. This library never checks a quantity or a price against either figure before sending an order.

The captured output below is the raw dictionary of public attributes, with carried_by trimmed to its first two entries. It was captured on 2026-09-26, before underlying_instrument_id was added, so that key, which would now appear as None for RELIANCE, is missing from it.

public_attributes = {}
for name, value in vars(reliance).items():
    if not name.startswith("_"):
        public_attributes[name] = value
print(public_attributes)
{'instrument_id': '3f92570a-9924-5bf5-9f9d-e006cd9f4202', 'exchange': 'nse', 'segment': 'nse_equities', 'shape': 'security', 'symbol': 'RELIANCE', 'underlying_symbol': None, 'expiry_date': None, 'strike_price': None, 'option_type': None, 'mapping_date': datetime.date(2026, 9, 26), 'first_seen_date': datetime.date(2026, 8, 7), 'last_seen_date': datetime.date(2026, 9, 26), 'lot_size': 1, 'tick_size': Decimal('0.1'), 'carried_by': [{'broker': 'dhan', 'broker_token': '2885', 'lot_size': '1.0', 'order_symbol': None, 'tick_size': '0.1'}, {'broker': 'flattrade', 'broker_token': '2885', 'lot_size': '1.0', 'order_symbol': 'RELIANCE-EQ', 'tick_size': None}, ...]}

shared_unified_broker_interface

classmethod Instrument.shared_unified_broker_interface()

This class method returns the one UBI client that every instrument, and the account, sends its requests through, creating it the first time it is asked. UBI holds a single access token for the whole application, so sharing one client keeps one cached copy of it and one place that reconnects after HTTP 401; One token for everyone has the rules. The client is stored on Instrument itself, so every subclass shares the same one.

Parameters

This method takes no parameters.

Example

The example below reads the shared client and asks UBI whether its session is connected. No output was captured for it.

client = reliance.shared_unified_broker_interface()
print(client.status())

Returns

The shared UnifiedBrokerInterface.

Raises

Exception When
ValueError The UBI base url is not configured, or the MongoDB settings document with UBI's key and secret is missing

Instrument

class Instrument(instrument_id=None, exchange=None, segment=None, symbol=None, underlying_symbol=None, expiry_date=None, strike_price=None, option_type=None, unified_broker_interface=None, details=None)GET /api/instruments/details

Instrument is the base of every instrument. It holds the lookup, the attributes above, prices, quote, last_price and ohlc, and it inherits the fourteen analysis classes described under Analysis, the last of which is the performance measures. You rarely build one directly; it is useful when all you have is an instrument_id, such as one read from an order row, and you do not care which class it belongs to.

Parameters

Name Type Required Default Description
instrument_id str or None One spelling None The instrument's UUID. When given, it is sent alone.
exchange str or None The other spelling None The exchange
segment str or None The other spelling None The segment, bare such as equities or prefixed such as nse_equities
symbol str or None For a security None The symbol
underlying_symbol str or None For a future or option None The underlying's symbol
expiry_date datetime.date, str or None For a future or option None The expiry
strike_price float or None For an option None The strike price
option_type str or None For an option None CE or PE
unified_broker_interface UnifiedBrokerInterface or None No None The client, or None for the shared one
details dict or None No None UBI's answer for this instrument from /api/instruments/details, such as one entry of a list request, used instead of looking the instrument up again. None sends the lookup.

Example

This example builds the same share by its id; the id is the one captured for RELIANCE above.

from tradingmachine.assets import instruments

same_share = instruments.Instrument(
    instrument_id="3f92570a-9924-5bf5-9f9d-e006cd9f4202",
)
assert same_share == reliance

Returns

The constructor returns an Instrument with the attributes above.

Raises

Exception When
InstrumentError UBI has no instrument matching the lookup
BadRequestError The lookup is incomplete or malformed, such as a future without an expiry_date or an id that is not a UUID
UnifiedBrokerInterfaceError Any other failure reported by, or on the way to, UBI

TradeableInstrument

class TradeableInstrument(...)GET /api/instruments/details

TradeableInstrument takes the same arguments as Instrument, including details, and adds everything that needs an order book or an account: the order-book values, orders, the price wrappers and positions. After the lookup it checks that the segment does not end in _indices, because an index cannot be traded.

Raises

Exception When
TradeableInstrumentError The instrument is an index
InstrumentError UBI has no instrument matching the lookup
BadRequestError The lookup is incomplete or malformed

NonTradeableInstrument

class NonTradeableInstrument(...)GET /api/instruments/details

NonTradeableInstrument takes the same arguments as Instrument, including details, and accepts only an index, which is any instrument whose segment ends in _indices. It has candles, quotes and analysis, but no order book and no order members, so reading nifty.best_bid is an AttributeError rather than a request.

Raises

Exception When
NonTradeableInstrumentError The instrument is not an index, so it can be traded
InstrumentError UBI has no instrument matching the lookup
BadRequestError The lookup is incomplete or malformed

constituents

property constituentsMongoDB asset_baskets, then POST /api/instruments/details

This property returns the stored asset basket of what an index or a fund holds, with the instrument itself as the basket's linked_instrument. It exists on NonTradeableInstrument, and so on all four index classes, and on ExchangeTradedFund and MutualFund. UBI stores no index constituents or fund holdings, so a basket exists only when one was saved with this instrument as its linked instrument, for instance by BasketCsvImporter. It is read from MongoDB and UBI on every access.

The instrument keeps its own official price, and the basket describes what it holds, so the two can be compared. On a fund, constituents is the fund's own portfolio, which is different from holdings, the units of the fund your account holds. A mutual fund has no candles in UBI, so its own performance measures return None and its constituents are the only way to measure it.

The table below lists which basket class each instrument's constituents usually returns.

Instrument Basket class
NonTradeableInstrument and the four index classes, such as EquityIndex Usually Index
ExchangeTradedFund ExchangeTradedFundConstituents
MutualFund MutualFundConstituents

The basket is rebuilt as whatever kind it was stored as, so the class depends on the stored document rather than on the instrument.

Example

The example below reads the NIFTY basket stored by the import on Asset baskets. Its output is the first part of the output captured there on 2026-09-28.

from tradingmachine.assets import equities

nifty = equities.EquityIndex(exchange="nse", symbol="NIFTY")
five = nifty.constituents
print(five)
Index(name='NIFTY FIVE', size=5)

Returns

The stored basket in effect today, as the AssetBasket subclass its stored kind names, or None when no basket is stored for this instrument.

Raises

Exception When
BasketMemberError UBI could not find one or more of the stored members
AssetBasketError The stored kind is not one the store knows
pymongo.errors.PyMongoError MongoDB could not be reached

The derivative base classes

class Derivative(...), Futures(...), Option(...), IndexFutures(...), IndexOption(...)GET /api/instruments/details

These five classes sit between TradeableInstrument and the sixteen futures and option classes, and hold what every contract of their kind shares. Their members are documented on Derivatives, and the discovery class methods they define are on Finding instruments. Each takes the same arguments as Instrument, including instrument_id, except that it adds underlying and has no details, and each checks what UBI returned. The table below lists what each one accepts.

Class Base Accepts
Derivative TradeableInstrument A future or an option with an expiry date, in one of the sixteen derivative segments
Futures Derivative A futures contract, on anything including an index
Option Derivative An option with a strike price and an option type, on anything including an index
IndexFutures Futures A futures contract in an index futures segment
IndexOption Option An option in an index options segment

You normally build a family class such as EquityOption. Building a base class directly is useful when you hold an instrument_id, from an order or a position row for example, and want the contract's members without knowing its family. The example below builds one from the id of a NIFTY future; its output was captured from a local UBI on 2026-09-28.

from tradingmachine.assets import equities, instruments

nifty_future = equities.EquityIndexFutures("nse", "NIFTY", "2026-09-29")
contract = instruments.Futures(instrument_id=nifty_future.instrument_id)
print(repr(contract))
print(contract == nifty_future)
print(type(contract.underlying).__name__)
Futures(exchange='nse', segment='nse_equity_index_futures', underlying_symbol='NIFTY', expiry_date='2026-09-29')
True
NonTradeableInstrument

Raises

Exception When
DerivativeError The instrument is not a future or an option, has no expiry date, or is in a segment with no known underlying segment and no underlying was given
TypeError underlying is given and is not an instrument
FuturesError, OptionError The contract is the other kind, or an option lacks a strike price or option type
IndexFuturesError, IndexOptionError The contract is not written on an index
InstrumentError UBI has no instrument matching the lookup
BadRequestError The lookup is incomplete or malformed

Lookup errors at a glance

The table below gathers every way building an instrument can fail, in the order the checks happen.

Step What fails Exception
1. Calling the constructor A required argument is missing TypeError, before any request
2. Creating the shared client No base url, or no key and secret in MongoDB ValueError
3. Sending the lookup UBI cannot be reached UnreachableError
4. UBI reads the parameters An unknown exchange or segment, or a missing identity field BadRequestError
5. UBI looks the instrument up Nothing matches on the latest mapping date The class's own error, such as EquityError, or InstrumentError for the base classes
6. The tradeable check The index-ness disagrees with the base class TradeableInstrumentError or NonTradeableInstrumentError
7. The derivative checks A derivative base class is given the wrong kind of contract DerivativeError, then the error of the class itself
Under the hood

The request is GET /api/instruments/details with instrument_id alone, or with every lookup argument that is not None. UBI decides which identity fields a segment's shape needs and answers 400 when some are missing, so this library keeps no segment table of its own. See Details on the UBI site for the route, and .claude/notes/src/tradingmachine/assets/instruments.py.md for the reasoning behind the lookup.