Skip to content

How it works

How it works

Four processes, one database, and an order lifecycle designed around a single awkward fact: a network can fail after a request has already been sent.

Four processes, and one boundary that matters

The web application handles requests. The worker handles time. Everything else follows from that line.

  • The web application

    Sign-in and sessions, settings, screens, read queries, and the creation of commands. It does not talk to a broker and it does not wait for one. A route that loops, polls or holds a socket is a bug here however well it is written, because a request that is still running is a request that cannot be redeployed, retried or reasoned about.
  • The worker

    Claims jobs, calls brokers, submits orders, reconciles what came back, backfills data and runs schedules. It is the only process allowed to take as long as it takes. It claims work one row at a time so two copies of the worker cannot pick up the same job.
  • The research service

    A separate Python service for features, backtest utilities and models. It may propose; it may never dispose — it cannot place an order, and anything it produces is re-validated by the risk layer that has no interest in where a number came from. Today it is a stub: nothing calls it yet.
  • PostgreSQL

    The source of truth for all three. Sessions, strategies, intents, orders, events, jobs and the audit trail are rows, not memory, so a restart loses nothing and two processes never disagree about what happened.

The life of an order

Steps one to five happen inside your request. Steps six to twelve happen whenever the worker reaches them, which may be seconds later. That gap is the reason risk is checked twice.

  1. Validate the input
    Against a schema, at the door.
  2. Authorise you, and scope the query
    Which organization you are acting in comes from your session, never from the request body.
  3. Create the order intent
    What was asked for, kept separate from what the broker eventually did, so a refused intent is still visible and still explicable months later.
  4. Run the pre-trade risk checks
    A fast, honest answer while you are still looking at the screen.
  5. Enqueue the execution job
    The intent, the idempotency record, the job and the audit entry are one transaction. They succeed together or not at all: a job without an intent executes nothing, and an intent without a job sits forever looking accepted.
  1. The worker claims the job
    One worker, one job, no double execution.
  2. Re-check risk and account state
    Between your request and this moment the price moved, an earlier order filled, the day’s loss grew, or you hit the kill switch. Checking only at request time checks a world that no longer exists. If the two checks disagree, this one wins.
  3. Submit to the broker
    With the key generated when you filled the form, so a double-click is one order rather than two.
  4. Persist exactly what came back
    The response, not a summary of it.
  5. Reconcile the status
    Including the case where nothing came back at all.
  6. Update the position
    From executions, not from intentions.
  7. Append the audit event
    Who asked, what was decided, and with which numbers.

When we do not know what happened

The state most order systems leave out, and the one that costs the most when it is missing.

  • Unknown is not rejected

    If the connection dies after a submission, the order may exist. Assuming it does not and retrying is how one outage becomes two positions. An order in this state never auto-retries; it queues a reconciliation against the broker’s own order book, and it stays loud on screen — not a spinner, and not "pending" — until that resolves or a human is told.
  • An entry and its stop are one operation

    A filled entry whose protective order failed is the most dangerous state this system can reach, so protection is tracked apart from the order status and can never be rendered as plain success. The alarm stands until somebody acknowledges it; it is not cleared by time or by scrolling past it.

What a stored order can explain

Months later, for any order, the record answers all of this — or the schema was wrong.

  • Who or what asked for it, and from which version of which strategy.
  • Which signal, computed on which bar, from which data source.
  • What the risk checks decided, with their numbers.
  • What was sent to the broker, and exactly what came back.
  • Every state it passed through, with timestamps.
  • If it is unresolved, what reconciliation has been attempted.

Brokers

One interface, several houses behind it, and nothing above that interface knows which one you use.

  • A demo broker that needs nothing

    No API key, no static IP, no market hours and no money — but a real adapter against the same contract, with prices that actually move and a switch that makes every read fail on purpose, so the "could not reach the broker" screen can be seen deliberately rather than discovered in production.
  • Zerodha Kite and Flattrade

    Both are implemented against their published contracts and both pass a shared conformance suite that drives them with a broker misbehaving on purpose: a 200 with an error body, a dropped connection, null fields, an unknown status code. Neither has yet been run against a live account.

No order has been placed from this application

Every adapter implements order placement and no route calls it. Reading this page as a description of something already trading would be a misreading — it describes a design and the parts of it that are built.