WEEK 1

UML & OOP for Real-Time Systems

A real-time system has no time to be surprised by its own code. Before you write a line of it, you name the objects, name the messages between them, and draw the picture — because at 3 p.m. on a live session, the diagram is the only thing that will tell you where to look.

Lab: introspect arena.Trader and draw its UML HW1: UML of all three abstract bases + a Level-1 bot The diagram is a deliverable, not a doodle
Part 1

1What is it? Objects, messages, and a picture of both

Object-oriented design is not about classes. It is about deciding what each part of the system is allowed to know. In a real-time system that decision has teeth: the boundaries you draw are the places you can later put a queue, a timeout, a retry, a metric, or a test — and the places you did not draw are the places you will later be unable to instrument.

Two questions, asked in this order

1. What are the nouns? Exchange, order book, order, trade, broker, trader, portfolio, tick. Each becomes a type with a single job and a name you would say out loud in a design review.

2. What crosses between them? Every arrow in your diagram is a message with a schema, and every message is a place where the system can be late, malformed, duplicated or lost. Objects are cheap; the arrows are where the engineering is.

Rule of thumb. If you cannot name the message, you have not found the boundary yet — you have found two halves of the same object.

Why UML, in 2026, before the code

Not for ceremony. For three concrete reasons: a class diagram fits on one page and a codebase does not; the arrow types force you to say whether a relationship is ownership, use, or kinship, which is exactly the decision people fudge in code; and a diagram is the artefact you hand a teammate at 3 p.m. when their bot has stopped filling.

You will draw three diagrams this quarter and each one has to survive contact with a live system. That is the test.

Class diagram

The static shape: types, their members, and how they relate. Answers “what exists, and who owns whom”. This is the week’s deliverable.

Sequence diagram

The dynamic shape: who sends what to whom, in what order, over time. Answers “what happened to my order”. Part 3 below animates one.

Both, together

A class diagram with no sequence diagram hides the latency; a sequence diagram with no class diagram hides the ownership. Real-time work needs the pair.

Interactive · build the class diagram

Add types, then join them with the five UML relationships that matter. The arrowhead is the argument: a hollow triangle claims kinship, a filled diamond claims ownership and a lifetime, a dashed arrow claims nothing but use. Press Load the arena SDK to see the diagram you will be asked to draw in the lab, then take it apart.

abstract / interface concrete class message / value object

    

Add a type

Add a relationship

Types
0
Relations
0
Abstract
0
Verdict
—
Part 2

2How does it work? Four pillars, then one interface, four behaviours

The four pillars are usually taught as vocabulary. Here each one is a load-bearing piece of a live system: pick a pillar, read what the arena does with it, and toggle between the version that has it and the version that does not.

—

In the arena

—

      

Interactive · one interface, four strategies, one price path

This is the Strategy pattern, and it is the reason the arena engine never contains an if strategy == "momentum". Four bots subclass the same base and implement the same single method. The loop below calls that method and nothing else — yet the equity curves fan out. Change the market regime and watch which behaviour was actually being rewarded.

StrategyP&LTradesMax DDSharpeWhat it believes

The market

The strategies

# The engine. It has never heard of momentum.
for tick in feed:
    for bot in self.bots:            # list[Trader]
        sig = bot.on_tick(market, portfolio)
        if sig is not None:
            self.exchange.submit(sig)

# Adding a fifth strategy touches zero lines above.
Part 3

3Why it matters: the life of one order, object by object

Here is the payoff, and it is a real-time payoff. One order crosses six objects and five message types on its way from on_tick to a changed number in your portfolio. Every hop is a place the design either lets you measure and test, or does not — and the total is the tick-to-ack latency you will be graded on in week 3.

Step
0 / 9
Elapsed
0.00 ms
Objects touched
0
Wire format
typed

The messages, in order

This step

Press Step

Nothing has happened yet. The bot is inside on_tick, holding a Signal it has not sent.


      
Part 4

4Five ways this goes wrong

Every one of these has shown up in a real submission. Open them.

Inheriting to reuse code, when you meant to hold an object

The tell is a base class with eleven methods, of which your subclass overrides two and inherits nine it does not want. class MyBot(Trader, RiskManager, Logger, ReconnectMixin) is not a design, it is a pile — and now the method resolution order decides your behaviour.

Inheritance says is a kind of, and it is a promise to callers that your object is substitutable for the base everywhere. If that promise is false, every polymorphic call site is a latent bug.

Fix: composition. self.risk = RiskManager(...) and call it. Inherit only to be substitutable; hold an object to reuse code. Ask: “would the engine be right to pass my object anywhere it passes the base?”

An abstract base class that forgot to be abstract

class Trader: with a def on_tick(self): pass is not a contract, it is a suggestion. Forget to implement it and you get silence: no exception, no signal, and a bot that politely does nothing for a whole session while you read the wrong logs.

ABC plus @abstractmethod turns that into a TypeError at construction — before the session opens, in the terminal, with your name on it.

Fix: class Trader(ABC) and exactly one @abstractmethod on_tick. Everything else gets a real default so an empty subclass is already a legal, running bot. Fail at import, never at 3 p.m.

A god object that knows the feed, the book, the risk and the socket

One 900-line TradingSystem class. It works, briefly. Then you cannot unit-test the matching logic without opening a socket, cannot measure decision latency separately from network latency, and cannot replay a session because the parsing and the deciding are the same method.

This is the pitfall with the highest cost in this course, because weeks 3, 6 and 9 all ask you to instrument or replay a stage in isolation. If the stages are not objects, they are not measurable.

Fix: one responsibility per type, and put pure logic (matching, signals, parsing) behind an interface with no I/O in it. shared/orderbook.py is the model: no sockets, no clock, 100% testable.

Mutable default arguments and shared class state

def __init__(self, symbols=[]): evaluates that list once, at function definition. Every bot you construct then shares it. The same trap with class-level attributes: class Bot: positions = {} gives every instance the same dict, so two bots in one process silently trade each other’s book.

It survives testing because tests usually build one object.

Fix: symbols=None then self.symbols = list(symbols or []), and every piece of per-instance state assigned inside __init__. If two of your bots run in one process, this bug is waiting for you.

A diagram that has drifted from the code

A UML diagram is a claim about the code, and a stale claim is worse than none: someone will trust it during an incident. Six weeks in, the diagram says Broker → Exchange and the code has grown a router in between.

Fix: keep the diagram in the repo next to the code it describes, regenerate or redraw it whenever a boundary moves, and make the design review read the diagram against the imports. In this course the diagram is a graded artefact for exactly that reason.

Part 5

5Check yourself

Three questions, instant feedback, nothing recorded.

Q1Your bot needs rate limiting. You already have a tested RateLimiter class. What is the right relationship?

Q2You are designing the trader base class. Nine hooks look useful. How many should be @abstractmethod?

Q3The exchange creates one OrderBook per symbol, holds them for its whole life, and they are destroyed with it. Which UML arrow?

Part 6

6Do the work

In the lab (~75 min)

  • Introspect arena.Trader from the code itself — inspect.getmembers, __abstractmethods__, __mro__ — and write down what the contract actually is.
  • Draw its UML class diagram, including the message types that cross its boundary.
  • Race two strategies behind that one interface in the offline simulator; explain the divergence without saying the word “luck”.

Homework 1

  • UML class diagrams for all three abstract bases: Trader, Broker, Exchange.
  • Defend, in a paragraph, why Trader declares exactly one @abstractmethod.
  • A Level-1 bot that connects, places one order, and logs its own object graph.
  • One-line AI-USE note on the deliverable.
Code
Get the code
Labs and homework live in your course’s own site; the starter repo is public. Use it as a template, clone, make install.
Course home
The 10-week map
Where week 1 sits, and the one loop every week instantiates.
Next
Week 2 · Concurrency & Networking →
Now make those objects run at the same time, and put a real byte stream between them.