Skip to content

Architecture

This tab explains how the library is put together and why. The short version is that tradingmachine is a thin layer of Python objects over one REST client, and everything that knows about brokers, prices and order types lives in the Unified Broker Interface (UBI), a separate service that runs on the same machine.

A useful way to picture it is a restaurant. Your program is the diner, the instrument objects are the menu, the shared client is the one waiter every table uses, UBI is the kitchen, and the ten brokers are the suppliers: the menu never cooks anything, it only tells the waiter what to ask the kitchen for.

From your program down to the brokers

The animation below shows the five layers between your program and a broker's server. The orange dots are reads, such as a quote, a set of candles or the positions, travelling up to your program. The blue dot is an order travelling down, through UBI's order engine, to one broker.

The layers between your program and the brokers Your program uses instrument, basket, order and account objects, which all send their requests through one shared UnifiedBrokerInterface client. The client calls UBI's REST API on 127.0.0.1:8080. UBI answers reads from its own Redis and TimescaleDB, which it keeps filled from the ten brokers, and hands orders to its order engine, which sends them to a broker. Orange dots are reads travelling up to your program; the blue dot is an order travelling down to a broker. Your program tradingmachine, inside your Python process Instrument, basket, order and account objects Equity, EquityIndexOption, Portfolio, BracketOrder, Account, ... UnifiedBrokerInterface, one client shared by every object one access token, reconnect on 401 reads come up orders go down Unified Broker Interface, a separate service on 127.0.0.1:8080 UBI REST API /api/instruments/* /api/portfolio/* /api/orders/* Redis and TimescaleDB quotes, candles, orders, positions Order engine resolves references, runs synthetic types Ten brokers Dhan, Flattrade, Fyers, Groww, INDmoney, Kotak, Shoonya, Stoxkart, Wisdom Capital, Zerodha
Orange dots are reads coming up from UBI's stores to your program. The blue dot is an order going down through the shared client, the REST API and the order engine to a broker.

The numbered list below describes each layer from the top, with where it lives.

  1. Instrument, basket, order and account objects are what your program builds and calls. An Equity, an EquityIndexOption or a CommodityFutures is one instrument; a Portfolio, a Watchlist or an Index is a basket of instruments; a BracketOrder or a TrailingStopOrder describes one synthetic order; an Account stands for the whole trading account. They live in tradingmachine.assets, tradingmachine.asset_baskets, tradingmachine.orders and tradingmachine.accounts. None of them holds any market data between calls; each read is a fresh request. Baskets are the one part that also keeps data of its own, in this project's MongoDB, because UBI stores no index constituents or fund holdings; Asset baskets describes them.
  2. The shared client, UnifiedBrokerInterface in tradingmachine.unified_broker_interface.client, is the only code that speaks HTTP. Every object in a process sends its requests through the same client, because UBI holds a single access token for the whole application, so one client keeps one cached copy of it and one place that reconnects. The client connects on first use, reconnects and retries once on HTTP 401, and turns each failure status into its own exception class.
  3. UBI's REST API listens on http://127.0.0.1:8080. It answers almost every read from its own Redis and TimescaleDB, which its background scripts keep filled from the brokers, so a read never waits for a broker. It is documented route by route on the UBI site.
  4. UBI's order engine is a separate UBI process that places every order on the REST API's behalf. It works out prices and quantities that were described rather than stated, holds plain limit orders until the book reaches their price, and runs the 53 synthetic order types, some of which keep placing orders long after your call has returned. See Order engine on the UBI site.
  5. The ten brokers are Dhan, Flattrade, Fyers, Groww, INDmoney, Kotak, Shoonya, Stoxkart, Wisdom Capital and Zerodha. UBI logs in to each of them and chooses which one sends a given order; this library never names a broker and never talks to one.

What the library does not do

The library keeps no cache, batches no date ranges, rounds no prices to the tick and checks no quantities against the lot size. UBI already does each of those, or holds the rule, and doing it again here would create a second copy that could drift. Design choices records each decision.

Which layer may talk to which

The rules below keep the layers independent. The table shows, for each part, what it is allowed to call.

Part Calls the shared client Calls UBI over HTTP Calls a broker Keeps state between calls
Your program through the objects, or directly for a route no object wraps Whatever it chooses
Instrument, order and account objects only through the client Only the instrument's identity, lot size and tick size, read once at construction
Basket objects and BasketStore only through the client The members and their weights or quantities, saved in this project's MongoDB
Shared client, UnifiedBrokerInterface it is the client the only code that does The access token and its expiry
UBI REST API it is the API for modifications, cancellations and flatten's cancels, and for a quote when no fresh one is cached Everything, in Redis, TimescaleDB and MongoDB
UBI order engine every placement Held limit orders and armed and working synthetic orders
Brokers The real orders, trades, positions and holdings

The one rule that matters most for a caller is the second row. An instrument object never opens its own connection, so any number of instruments, baskets, synthetic orders and an Account can live in one process and share one login. The instrument model explains how the client is shared.

A read and an order, side by side

The sequence below shows the two kinds of request the layers carry: a read of the last price, which UBI answers from memory, and an order that carries a price reference, which the order engine resolves and sends to a broker.

sequenceDiagram
    autonumber
    participant P as Your program
    participant O as Equity object
    participant C as Shared client
    participant A as UBI REST API
    participant E as Order engine
    participant B as Broker
    P->>O: share.last_price
    O->>C: get("/api/instruments/ltp")
    C->>A: GET with access-token
    A-->>C: last price from UBI's Redis
    C-->>O: parsed JSON
    O-->>P: 1226.0
    P->>O: share.buy_at_best_offer_price(quantity=1, product="cnc")
    O->>C: post("/api/orders/place") with price_reference
    C->>A: POST with access-token
    A->>E: hand the order over as an intent
    E->>E: read the quote, round to the tick
    E->>B: place the limit order
    B-->>E: order id
    E-->>A: answer with intent_id
    A-->>C: answer
    C-->>O: parsed JSON
    O-->>P: dict with broker, order_id, outcome

Every order takes the second path, through the engine. A plain limit order stops at the engine, which holds it until the book reaches its price; Order engine describes that and the parents the engine keeps.

Where to go next

The pages in this tab go deeper into each part of the design.

  • The instrument model


    The class hierarchy from Instrument to the 27 family classes, what lives at each level, and how the baskets, the synthetic orders and the account relate to it.

    The instrument model

  • Order engine


    What the library sends as descriptions rather than values, why a plain limit order is held, what a parent is, and how to read an answer that arrived late.

    Order engine

  • Design choices


    Twelve decisions that shape the library, each with its problem, its reasoning, its cost and where to see it in the code.

    Design choices

  • Repository structure


    Every package and module, how many there are, and which package imports which.

    Repository structure