Fixed income¶
The fixed income family covers bonds, interest rate indices, and the futures and options written on each of them. It lives in tradingmachine.assets.fixed_income and has six classes built exactly like the equity classes, with the same required identity fields and the same discovery class methods. What differs is what UBI knows about these instruments: a bond is named by its ISIN rather than a ticker, a cash bond has no quote, and nothing in the family has candles.
The table below lists the six classes.
| Kind | Class | What it is | UBI segment | Named by |
|---|---|---|---|---|
| class | FixedIncome |
A bond, debenture, government security or sovereign gold bond | fixed_income |
exchange, symbol |
| class | FixedIncomeFutures |
An interest rate future on a government security | fixed_income_futures |
exchange, underlying_symbol, expiry_date |
| class | FixedIncomeOption |
An interest rate option on a government security | fixed_income_options |
those three, plus strike_price, option_type |
| class | FixedIncomeIndex |
A rate index, such as ONMIBOR | fixed_income_indices |
exchange, symbol |
| class | FixedIncomeIndexFutures |
A future on a rate index | fixed_income_index_futures |
exchange, underlying_symbol, expiry_date |
| class | FixedIncomeIndexOption |
An option on a rate index, which no broker lists today | fixed_income_index_options |
those three, plus strike_price, option_type |
The cash segment is spelled fixed_income, not pluralised as equities is. That is UBI's own name for it and the class uses it as it is.
A bond is named by its ISIN¶
Almost every bond in UBI has an ISIN as its symbol, such as IN000126C010, rather than a ticker. This is the one place in UBI where that is so. The reason is that India's one-off corporate bonds and non-convertible debentures have tickers too inconsistent across brokers to match on, and the ISIN is the only identity that reconciles them.
The exception is about eighty interest rate underlyings on the nse, which are named by a rate code such as 633GS2035. They are few, but they matter most, because they are what the futures and options are written on. The table below shows the split as measured on 2026-09-20.
| Segment | Rows | Named by ISIN | Named by rate code |
|---|---|---|---|
nse_fixed_income |
6,816 | 6,737 | 79 |
bse_fixed_income |
14,614 | 14,614 | 0 |
Sovereign gold bonds are in this family too, named by their ISIN, rather than with commodities. FixedIncome.search is the easiest way to get from a half-remembered ISIN or rate code to a whole one, as the example below shows.
from tradingmachine.assets import fixed_income
by_isin = fixed_income.FixedIncome.search(exchange="bse", term="IN0001")
by_rate_code = fixed_income.FixedIncome.search(exchange="nse", term="GS2035")
bond = fixed_income.FixedIncome(exchange="nse", symbol="IN000126C010")
underlying = fixed_income.FixedIncome(exchange="nse", symbol="633GS2035")
On 2026-09-20 the first search returned six rows starting with IN000126C010, and the second returned six rows including 633GS2035, 664GS2035, 667GS2035 and R633GS2035. The rate underlyings carry no tick size from any broker, so their tick_size is None, as the table further down shows.
What UBI does not have for this family¶
Two gaps are large enough that the module states them in its own docstring, so that a raised error is not mistaken for a bug in the library. The flowchart below shows what happens when you ask each kind of fixed income instrument for its price.
flowchart TD
A["last_price"] --> B{"Which class?"}
B -->|"FixedIncome or FixedIncomeIndex"| C["Only Stoxkart carries it<br/>and Stoxkart serves no quotes"]
C --> D["ServiceUnavailableError"]
B -->|"the three derivative classes"| E["Quoted by Fyers, Stoxkart and Zerodha"]
E --> F["a price"]
G["prices(days=30)"] --> H["None for every class"]
A cash bond and a rate index have no quote. Only Stoxkart carries them, and Stoxkart does not serve quotes, so quote, last_price, ohlc and every order-book value raise ServiceUnavailableError. Stoxkart's order symbol for a cash bond is also empty, so there is no broker to send an order to either. Today FixedIncome is useful for identity, discovery and holdings, and not for prices or trading.
Nothing in this family has candles. prices(days=30) returned None for all seven contracts checked on 2026-09-20, including the derivatives that do have live quotes. The analysis methods are still present, because they come with Instrument, but they have nothing to work on here and return None. They will start working if UBI begins storing fixed income candles.
The table below shows the seven contracts checked on 2026-09-20. The prices are that day's.
| Class | Contract | Segment | lot_size |
tick_size |
Brokers | last_price |
|---|---|---|---|---|---|---|
FixedIncome |
nse IN000126C010 | nse_fixed_income |
1 | 0.01 | stoxkart | no quote |
FixedIncome |
nse 633GS2035 | nse_fixed_income |
1 | None | stoxkart | no quote |
FixedIncome |
bse IN000126C010 | bse_fixed_income |
1 | 0.01 | stoxkart | no quote |
FixedIncomeFutures |
633GS2035 2026-09-24 | nse_fixed_income_futures |
1 | 0.0025 | fyers, stoxkart, zerodha | 97.81 |
FixedIncomeOption |
633GS2035 2026-09-24 97.25 CE | nse_fixed_income_options |
None | 0.0025 | stoxkart, zerodha | 1.58 |
FixedIncomeIndex |
ONMIBOR | nse_fixed_income_indices |
1 | None | stoxkart | no quote |
FixedIncomeIndexFutures |
ONMIBOR 2026-09-30 | nse_fixed_income_index_futures |
1 | 0.0025 | fyers, stoxkart, zerodha | 5.3 |
The option's lot_size is None because the brokers do not agree on one.
The empty options segment¶
fixed_income_index_options is a name in UBI's list of segments that no broker fills, and nothing in UBI can currently put a row in it. FixedIncomeIndexOption exists anyway, so that the family has the same six classes as every other family with derivatives and so that it works the moment UBI gains a rule for it.
It fails cleanly rather than strangely. Building one raises FixedIncomeIndexOptionError, like any contract UBI does not have, and its discovery methods return empty results without raising, as the example below shows.
fixed_income.FixedIncomeIndexOption.expiries(exchange="nse", underlying_symbol="ONMIBOR") # []
fixed_income.FixedIncomeIndexOption.strikes(exchange="nse", underlying_symbol="ONMIBOR", expiry_date="2026-09-30") # []
fixed_income.FixedIncomeIndexOption.chain(exchange="nse", underlying_symbol="ONMIBOR", expiry_date="2026-09-30") # None
Finding derivative contracts¶
The derivative classes find their contracts the same way the equity ones do. The table below shows what the discovery calls returned on 2026-09-20.
| Call | Result |
|---|---|
FixedIncomeIndex.search(exchange="nse", term="MIBOR") |
1 row, ONMIBOR |
FixedIncomeFutures.expiries(underlying_symbol="633GS2035") |
2026-09-24, 10-29, 11-26, 12-31 and 2027-03-25 |
FixedIncomeOption.expiries(underlying_symbol="633GS2035") |
2026-09-24, 10-29, 11-26 and 12-31 |
FixedIncomeOption.strikes(expiry_date=2026-09-24) |
47 strikes, from 91.5 upward in steps of 0.25 |
FixedIncomeOption.chain(expiry_date=2026-09-24) |
94 rows, the 47 strikes as calls and puts |
FixedIncomeIndexFutures.expiries(underlying_symbol="ONMIBOR") |
six dates, 2026-09-30 to 2027-06-30 |
A row from the middle of that option chain, 633GS2035 2026-09-24 97.25 PE, was rebuilt into a FixedIncomeOption, and UBI returned the same instrument_id the row carried.
Holding a bond¶
FixedIncome carries the same six holdings members as Equity, copied into the class rather than shared, and no other class in this family has them. fixed_income is one of UBI's cash segments, so a bond is reported in the account's holdings exactly as a share is. The members always use the cnc product and sell only units that are not pledged. With a price, they send their limit order with hold=False, straight to a broker, because UBI's order engine would otherwise hold it until a live quote reached the price, and nothing quotes a cash bond; Order engine explains the rule. Holdings documents them.
Two differences from a share are worth knowing. A holding row's symbol and isin hold the same string here, because the symbol already is the ISIN, which makes matching a holding to its bond more reliable than it is for a share. And a bond held only at Groww is not reported at all, because UBI looks a Groww holding up by the broker's ticker among symbols that are ISINs, and never finds it.
No bond was held in the account when this was checked, so only the not-held path has been seen live. The three order methods were exercised against a recorder with invented holdings rows, and no order was sent.
A routing problem in UBI¶
UBI's own order routing for fixed income derivatives looks wrong, and it is recorded here so that the first person to place such an order knows where to start. It was found by reading UBI's source on 2026-09-20 and has not been confirmed with a real order.
UBI classifies fixed_income_futures, fixed_income_options and fixed_income_index_futures as securities, so it sends their orders to the equity derivatives venue, NFO at Zerodha. But the instruments come from the currency derivatives files, CDS at Zerodha and NSECD elsewhere, and Zerodha's own market table has a CDS entry that would be correct. The diagram below shows the mismatch.
flowchart LR
A["Rate future or option"] --> B["UBI classifies it<br/>as securities"]
B --> C["Order sent to NFO<br/>the equity derivatives venue"]
A --> D["Instrument listed in<br/>CDS or NSECD files"]
D -.->|"where it actually trades"| E["CDS<br/>currency derivatives venue"]
The same classification has two further effects. The quantity is sent in plain units rather than lots, although rate futures are traded in lots, and UBI's session window for these contracts closes at 16:00 although they trade until 17:00. The library does not work around any of this, because the fix belongs in UBI. Rate indices themselves are missing from UBI's list of tradeable segments, which agrees with FixedIncomeIndex being built on NonTradeableInstrument.
Because it is built on NonTradeableInstrument, FixedIncomeIndex also has the constituents property every index class has. It returns a basket only when one has been stored in this project's MongoDB for that index, and None otherwise.
The contract members¶
The four derivative classes inherit the derivative members, such as days_to_expiry, expiry_kind and contract_value, and those work here. The members that need the underlying's price work on the options and not on the futures, because no broker that serves quotes carries a bond or a rate index.
Futures have no underlying, options use a future
underlying_price, basis, basis_percent and cost_of_carry raise UnderlyingError on every fixed income future unless you give it an underlying when you build it, because no broker quotes a bond or a rate index and a future has no default underlying. An option needs nothing: it finds the rate future on the same underlying that expires first on or after it and prices with Black-76. On 2026-09-28 the 633GS2035 96.75 call for 29 October found the future of the same day at 96.83 and gave an implied volatility of 0.068.
Errors¶
The table below lists what each constructor raises. Each error is a subclass of InstrumentError and carries UBI's own message as its __cause__.
| Exception | When |
|---|---|
TypeError |
A required identity argument is missing. Python raises it before any request. |
FixedIncomeError |
UBI has no bond with that exchange and symbol, including a rate index asked for as a bond. |
FixedIncomeFuturesError |
UBI has no rate future with that underlying and expiry. |
FixedIncomeOptionError |
UBI has no rate option with those five fields. |
FixedIncomeIndexError |
UBI has no rate index with that symbol, including a bond asked for as an index. |
FixedIncomeIndexFuturesError |
UBI has no rate index future with that underlying and expiry. |
FixedIncomeIndexOptionError |
Always, today, because the segment is empty. |
ServiceUnavailableError |
A quote or order-book value is read on FixedIncome or FixedIncomeIndex. |