Skip to content

Latest commit

 

History

1 Commit

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Brue Connect

The broker door for Brue trade-entry scripts and for any platform. Brue is for writing trade entries (buy, sell, close) and connecting them to brokers; Brue Connect is the connecting half. A platform (a terminal, a strategy runner, a notebook, a Brue script) talks to a broker through an ADAPTER: a local process that speaks one line-delimited JSON protocol on stdio. Write that adapter once and your broker works in every platform built on this contract.

Any broker becomes one adapter. The reference paper simulator is an adapter with no special access, and nothing anywhere in the platform may special-case a broker by name.

Who this package is for. Two audiences, one contract:

  • Trading through a broker? You want a platform built on this contract (the LSE Terminal, or pip install brue-language for scripts), not this package directly; it comes along as a dependency and stays out of sight.
  • Integrating your broker or venue? You are in the right place. Read SPEC.md (the law), answer ONBOARDING.md, write one adapter, then prove it: pip install brue-connect and run the conformance suite against your adapter command. Nothing FAILs = your broker works in every platform on this contract.

Read this first

  1. SPEC.md is the contract, and it is normative. It is complete enough to implement an adapter in any language that can read and write lines of JSON: envelope, handshake, every method and event, the closed error set, reconciliation, and the safety requirements.
  2. ONBOARDING.md is the questionnaire to send a broker before anyone writes code. Every question in it exists because a real integration got that fact wrong quietly.
  3. This file is the practical part: the SDK, the environment, how to prove you are conformant, and how to get listed.

Install

pip install brue-connect

The SDK is a normal Python package with no dependencies. From a checkout, pip install -e /path/to/brue-connect gives the same result. Either way import brueconnect must resolve from anywhere, including the adapter you write in your own directory; if it does not, the conformance command below spawns an adapter that dies on its first line.

The shortest real adapter

Python adapters subclass Adapter and implement handle_<method> (dots become underscores: order.place is handle_order_place). The base class owns the stdio loop, dispatch, event sequencing, the handshake and the idempotency store, so you write broker logic and nothing else.

from brueconnect import Adapter, AdapterError

class MyBroker(Adapter):
    name, version, broker = "mybroker", "0.1", "My Broker"
    mode = "paper"                     # "live" means real money (SPEC 8.2)
    account_currency, account_model = "USD", "netting"

    # capabilities() is a METHOD you override, not a class attribute. It
    # returns the SPEC section 3 capabilities object; anything you leave out
    # is treated as a feature you do not have.
    def capabilities(self):
        return {"orders": {"market": True, "partial_close": False},
                "data": {"quotes": False, "bars": False, "history": False},
                "flatten_all": True}

    def on_first_call(self):
        """Log in HERE, never in your startup path: `hello` must stay
        answerable cold so a picker can learn your name for free."""

    def handle_catalog_list(self, params):
        return {"instruments": [...]}   # InstrumentSpec, SPEC section 4

    def handle_account_get(self, params):
        return {"balance": 0.0, "equity": 0.0, "margin_used": 0.0,
                "margin_free": 0.0, "currency": "USD"}

    def handle_positions_list(self, params):
        return {"positions": []}

    def handle_order_place(self, params):
        prior = self.idempotent_replay(params["client_id"])
        if prior is not None:
            return prior                # a replay places nothing
        # Conformance REQUIRES you to reject bad orders (SPEC 8.5), so a real
        # handler validates before it deals:
        #   unknown symbol       -> AdapterError("unknown_symbol", ...)
        #   qty off lot_step, or  <min_qty / >max_qty -> "invalid_qty"
        #   cannot afford margin -> "insufficient_margin"
        ...                             # deal it at your broker
        self.emit("order.filled", {...})
        return self.idempotent_store(params["client_id"], {"order_id": "1"})

    def handle_flatten_all(self, params):
        return {"closed": 0}            # REQUIRED of every adapter

    def handle_history_fills(self, params):
        return {"fills": []}            # REQUIRED: the ledger, oldest first

if __name__ == "__main__":
    MyBroker().run()

This is a skeleton, not a conformant adapter: it leaves out validation, a real catalog, and event handling. It shows the SHAPE. The conformance suite is what tells you when you are actually done.

idempotent_replay(client_id) returns the stored result of a client_id you have already seen (or None), and idempotent_store(client_id, result) records one; together they make a replayed order.place return its original outcome and place nothing. Raise AdapterError(code, message, retryable) inside a handler to return a typed error; code must come from the closed set in SPEC section 6, or carry an x_ prefix if it is yours. Use self.emit(name, data) for events; it is thread-safe. Guard your own state with your own locks (any name is fine; the SDK's own lock is private and cannot collide), and do not hold a lock across emit() longer than you must.

Working examples in this repo, smallest first:

Adapter Lines Why read it
tests/mini_adapter.py 84 the whole contract, one instrument
brueconnect/adapters/novafx/ 164 foreign vocabulary on a shared engine
brueconnect/adapters/paper/ 662 the reference adapter
brueconnect/adapters/meridian/ ~2000 a full integration: OAuth login, account selection, an at-least-once stream, REST as the backstop

brueconnect/adapters/ holds eight reference adapters for simulated venues (atlas, helix, meridian, northgate, pemberton, quantex, sablepoint, vertex), each speaking a genuinely different wire protocol (FIX, HMAC REST, a binary link, JSON-RPC over websocket, XML polling, STOMP, gRPC, OAuth2 REST), plus three more: paper (the reference simulator), novafx (foreign vocabulary on the paper engine) and lse_sim (the LSE demo account as a broker). All are pure standard library on purpose: an adapter is spawned by whatever Python the user's machine has, so a pip dependency makes that broker unconnectable for people who do not have it.

What the platform sets in your environment

Credentials never travel in a protocol message (SPEC 8.5). They reach you as environment variables at spawn:

Variable Meaning
BRUE_BROKER_STATE your private directory: credentials, tokens, caches. Yours alone, 0700, and it survives restarts
BRUE_CRED_<FIELD> what the user typed into your credential form, upper-cased (api_key becomes BRUE_CRED_API_KEY). Treat as optional
BRUE_ACCOUNT_ID which of the login's accounts this process deals on (SPEC 1.7). A platform sets it once the user has chosen; if it is empty your adapter falls back to the login's primary. This is your fallback, not the platform's default: a platform must never auto-pick an account for the user (SPEC 1.7), but your adapter must still do something sensible when spawned bare, e.g. in conformance

Anything else your adapter needs (an endpoint, a mode switch) is your own variable, set from the broker's directory row.

Prove it: the conformance suite

An adapter is conformant when the suite reports zero failures. Run it against your adapter exactly as a platform would spawn it:

python -m brueconnect.conformance.run --adapter "python3 path/to/my_adapter.py"

Useful flags:

  • --partial-hint SYMBOL:QTY a size known to fill partially, so the partial fill scenario can run instead of skipping.
  • --restartable your adapter persists its state, so the reconnect scenario may kill and relaunch it.
  • --live you accept that the suite will place REAL orders. It refuses to certify a non-paper account without this.
  • --auth-driver "cmd" for a broker with an interactive login: a command that plays the human and the browser, given the login URL as its last argument. Without it, an adapter that needs a login cannot be certified and the suite says so rather than failing noisily. See brueconnect/conformance/auth_drivers/meridian_login.py.

Give each run its own state directory, the way a real profile gets one:

BRUE_BROKER_STATE=/tmp/mybroker-state \
python -m brueconnect.conformance.run --adapter "python3 path/to/my_adapter.py"

Read the SKIPs. A skip is the suite honestly reporting a capability you did not declare; a skip you caused by declaring a capability false to dodge a failing scenario is not conformance, it is hiding.

Getting listed in a platform

Being conformant makes you connectable; a directory row makes you visible. The platform reads a runtime directory, so a broker is added or delisted without anyone updating an installed app. Your row carries:

Field What it is
display_name, tagline your name and one line about you
logo_svg, website your mark, rendered in the panel's ink
transport how you speak (rest_sse, fix, grpc, ...), display only
endpoint_host/port/path/tls where your adapter dials
credential_form the fields to collect from the user before connecting; empty when you need nothing typed

Two presentation contracts in SPEC say what your fields become on screen, so you can see the result before you send them: section 3.1 for your broker identity, and section 1.7 for your accounts.

The rules that matter most

These are the ones integrations get wrong quietly, so they are worth stating twice:

  • hello answers COLD. No login, no account read, no stream in your startup path. A picker handshakes you just to learn your name.
  • mode must be truthful, and it is per ACCOUNT once a login reaches several. Anything not paper is treated as real money and requires arming.
  • Anything absent from capabilities is absent from you. The platform refuses the feature loudly rather than emulating it silently.
  • Every money figure you report is already in the account's currency, at the rate you actually charge. The platform holds no exchange rates.
  • Idempotency on client_id for every mutating call: a replay returns the original outcome and places nothing.
  • flatten_all must work while everything else is failing or rate-limited.
  • Persist refresh tokens. A broker that keeps them in memory signs every client out each time it deploys.

Layout

SPEC.md                     the protocol (normative)
ONBOARDING.md               the questionnaire for a new broker
brueconnect/                the SDK: Adapter, Connector, protocol, registry
brueconnect/adapters/       eight simulated-venue adapters plus paper, novafx and lse_sim
brueconnect/conformance/    the suite that decides conformance
  auth_drivers/             stand-ins for a human at a login page
tests/                      SDK unit tests (python3 -m unittest discover -s tests)

About

Brue Connect: the broker connector for Brue.

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages