All reference chapters

7. Robot

A robot is the execution node at the top of a robot graph: it owns a set of rules, addressed by their labels, receives every order, amend, multileg and bracket request those rules emit, and forwards each one into the account's order management under its own label. Everything downstream — fills, reservations, journaled trades — is tagged with that label, which is what makes per-robot lifecycle control and per-robot results possible on an account running several robots at once. As everywhere in the API, C calls return TseStatus and leave the diagnostic on tse_last_error(account); the C++ wrapper throws tse::Error carrying that text; the Python wrapper raises tse.TseError.

Wiring a robot

The graph builds bottom-up and the robot closes it at the top: a pattern names the inputs it observes (the Pattern chapter), a rule names its pattern (the Rule chapter), and the robot names its rules — so tse_add_robot transitively takes ownership of a complete dependency component whose leaves are inputs. Nothing in that component processes market data yet: wiring only declares the graph, and the whole chain is put in motion by tse_start.

The robot is declared over rules that already exist: every entry of ruleLabels must name a rule previously built on this account, and an unknown rule label fails the whole call. The robot's label must be unique among the account's robots — re-adding under an existing label is an error — and, like every label, is capped at 63 characters (the appendix collects the boundary capacities). The set of contracts the robot trades is the union of its rules' contracts.

A robot submits through the account's single execution, so create one first with tse_exec_create_simulator or tse_exec_create_custom; starting a robot on an account without an execution is an error.

TseStatus tse_add_robot(TseAccountHandle account, char const* label,
                        char const* const* ruleLabels, size_t ruleCount);
void addRobot(std::string label, std::vector<std::string> ruleLabels) &;
account.add_robot(label, rule_labels)

Start, stop, halt

tse_start starts the robot's whole dependency component as one unit — every input, pattern and rule in it, then the robot itself, which attaches to order management for each traded contract and becomes active. Each traded contract is registered on the market adapters that carry it and auto-subscribed into the portfolio's mark-to-market, so a started robot's positions are marked without any manual tse_portfolio_subscribe. The claim is reference-counted per node: an input or pattern shared between two robots is started once and keeps running until the last robot using it stops.

tse_stop is the symmetric teardown: it releases every node of the component (honoring the reference counts, so shared nodes survive if another robot still holds them), detaches the robot from order management, marks it inactive and drains the robot's worker thread if it runs on its own core. tse_halt is the emergency brake: a noexcept flip of the active flag and nothing else — designed to never fail and to take effect immediately.

Neither call cancels an order or closes a position. After a halt the inputs, patterns and rules keep processing market data; the robot simply drops every rule output instead of forwarding it. Because the halted robot stays attached to order management, the bulk actions below still reach its orders and positions — which is exactly what the halt_and_* composites rely on.

The start fails loud, with the reason on the last-error channel, when the graph cannot run as a whole:

  • no execution has been created on the account;
  • a loaded input is still unbound — after tse_load, every input must be bound to a fresh market adapter with tse_bind_input first (the environment chapter);
  • the label names no robot on this account;
  • the component is incomplete — a runnable robot needs at least one rule, one pattern and one input beneath it;
  • under a Simulator execution, a traded contract sits on no price-carrying market adapter, so nothing could ever fill it;
  • a traded contract is already claimed by another started robot — a contract is traded by at most one robot at a time.

On any such failure nothing is left half-started: every node claimed so far is released again. A robot restored by tse_load is materialized on its first tse_start; until then it exists only as a recipe.

CallGraph nodesOMS attachmentWorking ordersPositions
tse_stopreleaseddetachedleft workinguntouched
tse_haltkeep runningkeptleft workinguntouched
TseStatus tse_start(TseAccountHandle account, char const* robotLabel);
TseStatus tse_stop (TseAccountHandle account, char const* robotLabel);
TseStatus tse_halt (TseAccountHandle account, char const* robotLabel);
void start(std::string robotLabel) &;
void stop(std::string robotLabel) &;
void halt(std::string robotLabel) &;
account.start(robot_label)
account.stop(robot_label)
account.halt(robot_label)

Bulk actions

Declared in tse_account.h, five bulk actions address a robot by label and sweep across all of its traded contracts.

tse_cancel_all cancels the robot's working orders contract by contract and releases their portfolio reservations; quantity already filled stays in the position. It reports an error while an amend of one of those orders is still pending. With nothing to cancel it succeeds as a no-op.

tse_sale_all flattens: for every traded contract holding a non-flat position it submits one exit order — market price, day time-in-force, quantity all, on the side opposite to the position — and skips flat contracts. It is idempotent (a second call finds nothing to close and succeeds), and it deliberately bypasses the active-flag gate, so it closes positions on a halted robot too. The resulting fills are journaled like any other trade, under the reserved rule label Position SaleAll. One documented refusal: a position whose exit is already resting at the venue makes tse_sale_all report an error rather than stack a second exit — cancel the resting order first.

The composites run their pieces in a fixed order — halt, then cancel-all, then (for the full variant) sale-all — and are equivalent to issuing those calls in sequence. The ordering is what resolves the resting-exit refusal above: tse_halt_and_cancel_sale_all first frees every position from its resting orders, then flattens it, which makes it the one-call flat-and-safe switch. A composite reports an error if any of its parts does, after attempting all of them.

C functionHalts the robotWorking ordersOpen positions
tse_cancel_allnocancelleduntouched
tse_sale_allnountouchedflattened
tse_halt_and_cancel_allyescancelleduntouched
tse_halt_and_sale_allyesuntouchedflattened
tse_halt_and_cancel_sale_allyescancelledflattened
TseStatus tse_cancel_all              (TseAccountHandle account, char const* robotLabel);
TseStatus tse_sale_all                (TseAccountHandle account, char const* robotLabel);
TseStatus tse_halt_and_cancel_all     (TseAccountHandle account, char const* robotLabel);
TseStatus tse_halt_and_sale_all       (TseAccountHandle account, char const* robotLabel);
TseStatus tse_halt_and_cancel_sale_all(TseAccountHandle account, char const* robotLabel);
void cancelAll(std::string robotLabel) &;
void saleAll(std::string robotLabel) &;
void haltAndCancelAll(std::string robotLabel) &;
void haltAndSaleAll(std::string robotLabel) &;
void haltAndCancelSaleAll(std::string robotLabel) &;
account.cancel_all(robot_label)
account.sale_all(robot_label)
account.halt_and_cancel_all(robot_label)
account.halt_and_sale_all(robot_label)
account.halt_and_cancel_sale_all(robot_label)

When updates fail

A pattern firing does not guarantee an order. Between a rule's output and an executed trade the engine drops or rejects the update at well-defined gates, each leaving the account consistent:

  • Stopped or halted robot. Rule outputs reaching an inactive robot are dropped before order management sees them; nothing reaches the execution and nothing is journaled.
  • Wrong contract. A rule is bound to one contract and reacts only to updates for it; market data for any other contract never fires the rule, whatever the pattern says.
  • Rules–positions mismatch. Every rule states in TseRuleParams.posSide the portfolio side that must hold for it to fire (the Rule chapter). An exit rule declared for a short position while the portfolio is long simply never fires; the entry rules that do match keep working unaffected.
  • Violated risk policy. Risk policies (the risk-management chapter) are checked at submit time, after the order's exposure is reserved. A violated policy rejects the order, and the rejection is atomic: every reservation the submission took — all legs of a multileg included — is rolled back. The robot stays active and processes the next signal normally.

Distinct from these clean rejections is the auto-halt: when a fault escapes while the robot dispatches into order management — an outgoing order, an amend, a multileg or a bracket — the robot halts itself immediately and logs the fault as critical. The resulting state is exactly that of tse_halt: graph running, orders resting, positions kept, bulk actions available for cleanup.

Reading results

Per-robot results come from the same two readers the account offers globally (the Account chapter), narrowed by the robot's label: tse_get_robot_summary folds a TseSummary over that robot's trades only; tse_get_summaries fills one summary per robot in tse_add_robot order. tse_get_trades with a robot label returns only the trades that robot produced — every journaled TseTrade carries the robotLabel and the ruleLabel that produced it, so a robot's history can be split further by rule, including the Position SaleAll flattening fills. The summary fields, the trade record and the capacity-in / count-out reading protocol are documented in the Account chapter.

TseStatus tse_get_robot_summary(TseAccountHandle account, char const* robotLabel, TseSummary* outSummary);
TseStatus tse_get_trades(TseAccountHandle account, int64_t fromNanoseconds, int64_t toNanoseconds,
                         char const* robotLabel, TseTrade* outTrades, size_t* inoutCount);
Summary getRobotSummary(std::string robotLabel) const &;
std::vector<Summary> getSummaries() const &;
std::vector<Trade> getTrades(std::int64_t fromNanoseconds = 0, std::int64_t toNanoseconds = 0,
                             std::string robotLabel = std::string()) const &;
summary   = account.get_robot_summary(robot_label)
summaries = account.get_summaries()
trades    = account.get_trades(from_nanoseconds=0, to_nanoseconds=0, robot_label=robot_label)

Version 5.0.0.0