Quickstart

Install the SDK, set your keys, connect a practice broker and send a first order through reTrade.

In development. Not released yet; names and formats may change.View as Markdown

Install

Terminal
pip install retrade==0.1.2
Terminal
npm install retrade@0.1.2

The package is retrade on PyPI and on npm. Python needs 3.12 or later; Node needs 22.18 or later. Both install a retrade command. The SDK is pre-release, so pin the version you test with.

Get your keys

You need four things. Your admin and reTrade give you three; you make the fourth.

What the SDK needs, and where each comes from
WhatLooks likeWhere it comes from
Executor key fileexecutor.pemYou make it, below, and keep it. reTrade only ever sees its public half.
Bot key idexk_test_ and 26 more charactersYour admin, after registering your key.
Root public key43 charactersreTrade, when your tenant is set up.
Account refthe name your admin gave the accountYour admin.
Terminal
retrade keygen executor.pem
Terminal
npx retrade keygen executor.pem

This writes a new private key, readable only by you, and prints its public half. Send only the public half to your admin. They register it for one mode and send back your bot key id. Start with a test key: its orders are checked like real ones, but its decisions aren’t evidence for an insurer. Keep executor.pem secret: anyone holding it can ask for decisions as your agent.

What your admin runs
# Your admin runs this with their own admin key.
# The admin command line is in development.
retrade-admin keys add-executor --mode test <public key>

Set your environment

Terminal
export RETRADE_EXECUTOR_KEY_ID="<bot key id from your admin>"
export RETRADE_EXECUTOR_KEY_PATH="$PWD/executor.pem"
export RETRADE_ROOT_KEY="<root public key from reTrade>"
export RETRADE_ACCOUNT="<account ref from your admin>"
export RETRADE_AGENT="<this agent's id>"

These names are a convention for your own code: the samples read them and pass the values to the SDK. The SDK itself reads one variable, RETRADE_BASE_URL, and only when you pass no address. Without either, it uses reTrade’s hosted service, https://api.retrade.app.

Connect a practice broker

The SDK reads the account and places orders only through an adapter: four functions you write against your broker’s API. For a first run, this one keeps a pretend account in memory and records orders instead of sending them anywhere.

Python
from datetime import UTC, datetime
from decimal import Decimal

import retrade

placed: list[str] = []  # the tags of the orders this pretend broker took


def read_account(
    instrument: str | None, adjustments_since: datetime | None
) -> tuple[retrade.Snapshot, retrade.Quote | None]:
    now = datetime.now(UTC)
    snapshot = retrade.Snapshot(
        as_of=now,
        cash={"USD": Decimal("50000")},
        settled_cash={"USD": Decimal("50000")},
        positions=(),
        open_orders=(),
    )
    if instrument is None:
        return snapshot, None
    quote = retrade.Quote(instrument, Decimal("187.10"), Decimal("187.08"),
                          Decimal("187.12"), now)
    return snapshot, quote


def submit(order: retrade.ApprovedOrder, tag: str) -> str:
    placed.append(tag)
    return f"paper-{len(placed)}"


def find(tag: str, order: retrade.ApprovedOrder, since: datetime) -> str | None:
    return f"paper-{placed.index(tag) + 1}" if tag in placed else None


def cancel(broker_order_id: str) -> None:
    pass  # nothing rests at this pretend broker


broker = retrade.GenericAdapter(
    snapshot=read_account, submit=submit, find=find, cancel=cancel,
    g_seconds=60, converts_cash=False,
)
TypeScript
import { GenericAdapter } from "retrade";

const placed: string[] = []; // the tags of the orders this pretend broker took

const broker = new GenericAdapter({
  snapshot(instrument) {
    const asOf = new Date();
    const snapshot = {
      asOf,
      cash: { USD: "50000" },
      settledCash: { USD: "50000" },
      positions: [],
      openOrders: [],
    };
    const quote = instrument === null
      ? null
      : { instrument, last: "187.10", bid: "187.08", ask: "187.12", asOf };
    return { snapshot, quote };
  },
  submit(order, tag) {
    placed.push(tag);
    return `paper-${placed.length}`;
  },
  find(tag) {
    const index = placed.indexOf(tag);
    return index === -1 ? null : `paper-${index + 1}`;
  },
  cancel() {
    // nothing rests at this pretend broker
  },
  gSeconds: 60,
  convertsCash: false,
});

The adapter’s g_seconds and converts_cash (gSeconds and convertsCash in TypeScript) must equal the account’s settings at reTrade; 60 and false are the SDK’s defaults. Ask your admin if the account uses others. When you’re ready, swap in your real broker: see Connect your broker.

Run your first check

Python
import os
from decimal import Decimal

from retrade import Client, Order, OrderType, Side, Tif

with Client(
    os.environ["RETRADE_EXECUTOR_KEY_ID"],
    os.environ["RETRADE_EXECUTOR_KEY_PATH"],
    root_key=os.environ["RETRADE_ROOT_KEY"],
) as rt:
    order = Order("XNAS:AAPL", Side.BUY, 40, OrderType.LIMIT, Tif.DAY,
                  limit_price=Decimal("187.25"))
    result = rt.place(order, account=os.environ["RETRADE_ACCOUNT"],
                      agent=os.environ["RETRADE_AGENT"], via=broker)
    why = result.deciding or result.reason
    print(result.outcome, result.placement, result.quantity, why)
TypeScript
import { Client } from "retrade";

function env(name: string): string {
  const value = process.env[name];
  if (!value) throw new Error(`${name} is not set`);
  return value;
}

const rt = await Client.open({
  keyId: env("RETRADE_EXECUTOR_KEY_ID"),
  keyPath: env("RETRADE_EXECUTOR_KEY_PATH"),
  rootKey: env("RETRADE_ROOT_KEY"),
});
try {
  const order = {
    instrument: "XNAS:AAPL", side: "buy", quantity: 40,
    type: "limit", tif: "day", limitPrice: "187.25",
  } as const;
  const result = await rt.place(order, {
    account: env("RETRADE_ACCOUNT"), agent: env("RETRADE_AGENT"), via: broker,
  });
  const why = result.deciding ?? result.reason;
  console.log(result.outcome, result.placement, result.quantity, why);
} finally {
  await rt.close();
}

Save the practice broker and this check in one file. In Python, as first_check.py, run with python first_check.py. In TypeScript, as first-check.mts, run with node first-check.mts: Node 22.18 and later run TypeScript files directly.

Read the answer

place returns a result. A BLOCK, or reTrade not answering, is a result too, never an exception. The fields you’ll use first:

The first fields of a result
FieldWhat it tells you
outcomeALLOW, CUT or BLOCK.
placementplaced, not_placed, or unknown when the broker call’s outcome isn’t known. Never send an unknown order again.
quantityWhat was approved: smaller than you asked on a CUT.
decidingOn a CUT or BLOCK, the rule that decided, such as {"code": "order_size_cap"}.
fitsOn a BLOCK over a dollar line or in an agent conflict, the largest quantity that would pass.
reasonThe SDK’s own reason when it decided without reTrade, such as unreachable.
decision_id (decisionId)The receipt’s id. Store it with your own order record.

If reTrade doesn’t answer within 2.5 seconds, you get BLOCK with reason unreachable, and nothing is placed. That’s fail-closed.

Next steps