<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://wiki.spiretrading.com/index.php?action=history&amp;feed=atom&amp;title=State_Machine_Specification</id>
	<title>State Machine Specification - Revision history</title>
	<link rel="self" type="application/atom+xml" href="https://wiki.spiretrading.com/index.php?action=history&amp;feed=atom&amp;title=State_Machine_Specification"/>
	<link rel="alternate" type="text/html" href="https://wiki.spiretrading.com/index.php?title=State_Machine_Specification&amp;action=history"/>
	<updated>2026-07-28T17:52:31Z</updated>
	<subtitle>Revision history for this page on the wiki</subtitle>
	<generator>MediaWiki 1.34.1</generator>
	<entry>
		<id>https://wiki.spiretrading.com/index.php?title=State_Machine_Specification&amp;diff=218&amp;oldid=prev</id>
		<title>Kamal: Kamal moved page State Machine Specifications to State Machine Specification without leaving a redirect</title>
		<link rel="alternate" type="text/html" href="https://wiki.spiretrading.com/index.php?title=State_Machine_Specification&amp;diff=218&amp;oldid=prev"/>
		<updated>2026-07-13T19:05:32Z</updated>

		<summary type="html">&lt;p&gt;Kamal moved page &lt;a href=&quot;/index.php?title=State_Machine_Specifications&amp;amp;action=edit&amp;amp;redlink=1&quot; class=&quot;new&quot; title=&quot;State Machine Specifications (page does not exist)&quot;&gt;State Machine Specifications&lt;/a&gt; to &lt;a href=&quot;/index.php/State_Machine_Specification&quot; title=&quot;State Machine Specification&quot;&gt;State Machine Specification&lt;/a&gt; without leaving a redirect&lt;/p&gt;
&lt;table class=&quot;diff diff-contentalign-left&quot; data-mw=&quot;interface&quot;&gt;
				&lt;col class=&quot;diff-marker&quot; /&gt;
				&lt;col class=&quot;diff-content&quot; /&gt;
				&lt;col class=&quot;diff-marker&quot; /&gt;
				&lt;col class=&quot;diff-content&quot; /&gt;
				&lt;tr class=&quot;diff-title&quot; lang=&quot;en&quot;&gt;
				&lt;td colspan=&quot;2&quot; style=&quot;background-color: #fff; color: #222; text-align: center;&quot;&gt;← Older revision&lt;/td&gt;
				&lt;td colspan=&quot;2&quot; style=&quot;background-color: #fff; color: #222; text-align: center;&quot;&gt;Revision as of 19:05, 13 July 2026&lt;/td&gt;
				&lt;/tr&gt;
&lt;!-- diff cache key mediawikidb-mediawiki_:diff::1.12:old-215:rev-218 --&gt;
&lt;/table&gt;</summary>
		<author><name>Kamal</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.spiretrading.com/index.php?title=State_Machine_Specification&amp;diff=215&amp;oldid=prev</id>
		<title>Kamal: Created page with &quot; Every strategy and order type begins as a state machine specification, a GraphViz file containing the machine's graph and a formal legend defining its parameters, states, eve...&quot;</title>
		<link rel="alternate" type="text/html" href="https://wiki.spiretrading.com/index.php?title=State_Machine_Specification&amp;diff=215&amp;oldid=prev"/>
		<updated>2026-07-13T18:08:31Z</updated>

		<summary type="html">&lt;p&gt;Created page with &amp;quot; Every strategy and order type begins as a state machine specification, a GraphViz file containing the machine&amp;#039;s graph and a formal legend defining its parameters, states, eve...&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;New page&lt;/b&gt;&lt;/p&gt;&lt;div&gt;&lt;br /&gt;
Every strategy and order type begins as a state machine specification, a GraphViz file containing the machine's graph and a formal legend defining its&lt;br /&gt;
parameters, states, events, and conditions. The specification is designed and&lt;br /&gt;
reviewed before any code is written, and the Python implementation is a&lt;br /&gt;
mechanical, one-to-one transcription of it. If a behavior can't be pointed to&lt;br /&gt;
in the specification, it doesn't belong in the implementation.&lt;br /&gt;
&lt;br /&gt;
This article defines the specification format and its semantics. The running example is &amp;lt;code&amp;gt;PeggedOrder&amp;lt;/code&amp;gt;, an order that trades passively at a price pegged to the BBO quote's price, repricing as the BBO quote moves away and honoring the order's limit price.&lt;br /&gt;
&lt;br /&gt;
== Anatomy ==&lt;br /&gt;
&lt;br /&gt;
A specification is a single &amp;lt;code&amp;gt;.gv&amp;lt;/code&amp;gt; file with two sections:&lt;br /&gt;
&lt;br /&gt;
# A &amp;lt;code&amp;gt;digraph&amp;lt;/code&amp;gt; describing the machine: one node per state, one edge per transition, edges labeled by the events and conditions that perform them.&lt;br /&gt;
# A legend after the closing brace defining the parameters, each state's actions, each event, and each condition.&lt;br /&gt;
&lt;br /&gt;
== The example ==&lt;br /&gt;
&lt;br /&gt;
[[File:Pegged order.png|thumb|center|700px|The PeggedOrder state machine.]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
digraph PeggedOrder {&lt;br /&gt;
  rankdir = LR;&lt;br /&gt;
  node [shape = circle];&lt;br /&gt;
&lt;br /&gt;
  S0 [color = green, fontcolor = green];&lt;br /&gt;
  S3 [color = red, fontcolor = red];&lt;br /&gt;
  S6 [color = blue, fontcolor = blue];&lt;br /&gt;
&lt;br /&gt;
  S0 -&amp;gt; S1 [label = &amp;quot;E0&amp;quot;, weight = 100];&lt;br /&gt;
  S0 -&amp;gt; S6 [label = &amp;quot;E5&amp;quot;];&lt;br /&gt;
  S1 -&amp;gt; S6 [label = &amp;quot;C0&amp;quot;];&lt;br /&gt;
  S1 -&amp;gt; S2 [label = &amp;quot;&amp;amp;amp;#949;&amp;quot;, weight = 100];&lt;br /&gt;
  S2 -&amp;gt; S3 [label = &amp;quot;E2&amp;quot;];&lt;br /&gt;
  S2 -&amp;gt; S4 [label = &amp;quot;E3&amp;quot;, weight = 100];&lt;br /&gt;
  S2 -&amp;gt; S7 [label = &amp;quot;E5&amp;quot;];&lt;br /&gt;
  S4 -&amp;gt; S4 [label = &amp;quot;E0&amp;quot;];&lt;br /&gt;
  S4 -&amp;gt; S5 [label = &amp;quot;C1&amp;quot;];&lt;br /&gt;
  S4 -&amp;gt; S1 [label = &amp;quot;E4&amp;quot;];&lt;br /&gt;
  S4 -&amp;gt; S8 [label = &amp;quot;E5&amp;quot;];&lt;br /&gt;
  S5 -&amp;gt; S1 [label = &amp;quot;E4&amp;quot;];&lt;br /&gt;
  S5 -&amp;gt; S8 [label = &amp;quot;E5&amp;quot;];&lt;br /&gt;
  S7 -&amp;gt; S8 [label = &amp;quot;E3&amp;quot;];&lt;br /&gt;
  S7 -&amp;gt; S6 [label = &amp;quot;E4&amp;quot;];&lt;br /&gt;
  S8 -&amp;gt; S6 [label = &amp;quot;E4&amp;quot;];&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
Parameters:&lt;br /&gt;
  clients (Clients): The clients used to access Nexus services.&lt;br /&gt;
  order_fields (OrderFields): The specification of the order to execute.&lt;br /&gt;
  peg_difference (Money): The difference applied to the pegged price.&lt;br /&gt;
&lt;br /&gt;
S0:&lt;br /&gt;
  filled_quantity = 0&lt;br /&gt;
  bbo_quotes = clients.market_data_client.query_bbo_quotes(&lt;br /&gt;
    make_current_query(order_fields.ticker))&lt;br /&gt;
S2:&lt;br /&gt;
  submission_price = peg_price&lt;br /&gt;
  fields = OrderFields(order_fields)&lt;br /&gt;
  fields.type = OrderType.LIMIT&lt;br /&gt;
  fields.price = submission_price&lt;br /&gt;
  fields.quantity = order_fields.quantity - filled_quantity&lt;br /&gt;
  order = clients.order_execution_client.submit(fields)&lt;br /&gt;
S5:&lt;br /&gt;
  clients.order_execution_client.cancel(order)&lt;br /&gt;
S8:&lt;br /&gt;
  clients.order_execution_client.cancel(order)&lt;br /&gt;
&lt;br /&gt;
E0 - bbo_quotes.pop(quote):&lt;br /&gt;
  bbo = quote&lt;br /&gt;
  peg_price = pick(order_fields.side, bbo.ask.price, bbo.bid.price) -&lt;br /&gt;
    direction(order_fields.side) * peg_difference&lt;br /&gt;
  if order_fields.price != 0:&lt;br /&gt;
    peg_price = pick(order_fields.side,&lt;br /&gt;
      max(peg_price, order_fields.price),&lt;br /&gt;
      min(peg_price, order_fields.price))&lt;br /&gt;
E1 - order.execution_report(report):&lt;br /&gt;
  filled_quantity = filled_quantity + report.last_quantity&lt;br /&gt;
E2 - E1 if report.status == OrderStatus.REJECTED&lt;br /&gt;
E3 - E1 if report.status == OrderStatus.NEW&lt;br /&gt;
E4 - E1 if is_terminal(report.status)&lt;br /&gt;
E5 - cancel()&lt;br /&gt;
&lt;br /&gt;
C0 - filled_quantity == order_fields.quantity&lt;br /&gt;
C1 - pick(order_fields.side, submission_price &amp;gt; peg_price,&lt;br /&gt;
    submission_price &amp;lt; peg_price)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Reading it end to end: S0 subscribes to the BBO and waits for the first&lt;br /&gt;
quote. S1 decides between done (C0) and submitting (&amp;amp;#949;). S2 submits a&lt;br /&gt;
limit order at the pegged price. S4 rests, repricing through S5 whenever the&lt;br /&gt;
market moves away (C1). Any terminal report returns to S1, which resubmits&lt;br /&gt;
the remainder or finishes. S3 is the rejected terminal, S6 the normal&lt;br /&gt;
terminal, and S7/S8 handle cancellation, including waiting out an order that&lt;br /&gt;
is still pending acceptance.&lt;br /&gt;
&lt;br /&gt;
== The graph ==&lt;br /&gt;
&lt;br /&gt;
States are circles named &amp;lt;code&amp;gt;S0&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;S1&amp;lt;/code&amp;gt;, ... and numbered in '''flow order''': the&lt;br /&gt;
first place the machine goes gets the first number. The start state is green.&lt;br /&gt;
Red marks a terminal state reached through an actual error, ie. a rejection, and blue marks a completed state. Cancels are orderly outcomes, not errors and should typically transition to a blue state.&lt;br /&gt;
&lt;br /&gt;
Edges are labeled with the conditions and events that perform the&lt;br /&gt;
transition. When two or more of them share the same source and target, they&lt;br /&gt;
share one edge with a comma label. Conditions should be labeled before events, and each in numeric order (&amp;lt;code&amp;gt;&amp;quot;C1, E0&amp;quot;&amp;lt;/code&amp;gt;). Merging is purely notational.&lt;br /&gt;
&lt;br /&gt;
The graph should render the way the machine runs, the start state is the&lt;br /&gt;
left-most node and the machine's natural progression reads from left to&lt;br /&gt;
right, with the default path sitting together on a single row. Deviations&lt;br /&gt;
from the main path sit above or below it, with the expectation that they&lt;br /&gt;
eventually loop back into the main path or reach a terminal state. Use the various graphviz/dot notation to tune the rendering with attributes such as &amp;lt;code&amp;gt;weight&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;constraint&amp;lt;/code&amp;gt; to keep states level.&lt;br /&gt;
&lt;br /&gt;
== Parameters ==&lt;br /&gt;
&lt;br /&gt;
The legend begins with a &amp;lt;code&amp;gt;Parameters&amp;lt;/code&amp;gt; section listing every parameter as&lt;br /&gt;
&amp;lt;code&amp;gt;name (Type): description&amp;lt;/code&amp;gt;. Descriptions state the parameter's role, refrain from restating a description of the type itself.&lt;br /&gt;
&lt;br /&gt;
== States ==&lt;br /&gt;
&lt;br /&gt;
Each state with an action gets a legend entry: a bare &amp;lt;code&amp;gt;S#:&amp;lt;/code&amp;gt; header followed&lt;br /&gt;
by two-space-indented lines of Python-like pseudocode. Only actions and&lt;br /&gt;
updates appear, refrain from no prose titles or explanatory notes. States without actions have no entry.&lt;br /&gt;
&lt;br /&gt;
Actions should be written using Python-like API syntax to the extent possible: free functions as free functions (&amp;lt;code&amp;gt;load_ticker_info(clients.market_data_client, ticker)&amp;lt;/code&amp;gt;), methods as methods (&amp;lt;code&amp;gt;clients.make_timer(period)&amp;lt;/code&amp;gt;), enum constants&lt;br /&gt;
fully qualified (&amp;lt;code&amp;gt;OrderStatus.REJECTED&amp;lt;/code&amp;gt;), access paths written in full&lt;br /&gt;
through &amp;lt;code&amp;gt;clients&amp;lt;/code&amp;gt;, and module prefixes omitted. The formal syntax is not&lt;br /&gt;
obligated to mirror Python exactly, you can omit various plumbing and boilerplate code and focus on the idealized semantics of the state machine. For example the PeggedOrder's S0 subscription binds its stream as a value:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
bbo_quotes = clients.market_data_client.query_bbo_quotes(&lt;br /&gt;
    make_current_query(order_fields.ticker))&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
rather than the literal declare-a-queue-and-pass-it-in shape of the binding.&lt;br /&gt;
&lt;br /&gt;
== Variables ==&lt;br /&gt;
&lt;br /&gt;
Variables are snake_case, never containing whitespace, and every one is defined explicitly and initialized in the start state's action, derived by formula, or updated in the body of the event that changes them. A derived variable that must track a stream is computed in that stream's event body, &amp;lt;code&amp;gt;peg_price&amp;lt;/code&amp;gt; is recomputed on every quote in E0, so the submit state and the reprice condition read one variable and can never disagree about the cap.&lt;br /&gt;
&lt;br /&gt;
== Events ==&lt;br /&gt;
&lt;br /&gt;
An event is declared as:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
E&amp;lt;n&amp;gt; - &amp;lt;source&amp;gt;.&amp;lt;occurrence&amp;gt;(&amp;lt;binding&amp;gt;) [if &amp;lt;guard&amp;gt;] [:&amp;lt;body&amp;gt;]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* '''Source''' — an expression evaluating to an object that publishes events (ie. &amp;lt;code&amp;gt;bbo_quotes&amp;lt;/code&amp;gt; from S0, &amp;lt;code&amp;gt;order&amp;lt;/code&amp;gt; from S2). An event's source must be bound before any state with an edge on that event. Sources may be rebound; the event follows the variable.&lt;br /&gt;
* '''Occurrence''' — the name of the event being published (&amp;lt;code&amp;gt;pop&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;execution_report&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;expired&amp;lt;/code&amp;gt;). The occurrence names ''what'' arrived.&lt;br /&gt;
* '''Binding''' — The event's payload, one or more names that capture the data associated with the event (&amp;lt;code&amp;gt;(report)&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;(quote)&amp;lt;/code&amp;gt;). If the payload is unused, name it &amp;lt;code&amp;gt;_&amp;lt;/code&amp;gt;. Empty payloads are represented as &amp;lt;code&amp;gt;()&amp;lt;/code&amp;gt;.&lt;br /&gt;
* '''Guard''' — a Python expression that can be used to match only a subset of events. Introduced with an &amp;lt;code&amp;gt;if&amp;lt;/code&amp;gt;, matching &amp;lt;code&amp;gt;match&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;case&amp;lt;/code&amp;gt; precedent. Position disambiguates it from a body conditional.&lt;br /&gt;
* '''Body''' — updates and actions, same pseudocode rules as states. Events may perform actions (restart a timer), but always as precise pseudocode, never prose.&lt;br /&gt;
&lt;br /&gt;
A '''refinement''' &amp;lt;code&amp;gt;E&amp;lt;m&amp;gt; - E&amp;lt;n&amp;gt; [if &amp;lt;guard&amp;gt;]&amp;lt;/code&amp;gt; is its base restricted: it&lt;br /&gt;
inherits the base's source and binding, and guards conjoin. When a refined&lt;br /&gt;
event happens its base has happened as well — every REJECTED report ''is'' an&lt;br /&gt;
execution report — so the base's body executes too. Refinements kill&lt;br /&gt;
repeated signatures, which are an opportunity for errors, and they force the&lt;br /&gt;
base to be declared before its specializations.&lt;br /&gt;
&lt;br /&gt;
A '''global event''' has no source object: &amp;lt;code&amp;gt;E5 - cancel()&amp;lt;/code&amp;gt; is the order's own&lt;br /&gt;
cancel request, delivered through the same serialized queue as everything&lt;br /&gt;
else.&lt;br /&gt;
&lt;br /&gt;
The '''generic-event pattern''': an update that applies to every occurrence&lt;br /&gt;
of an event gets its own numbered event rather than a prose note — E1&lt;br /&gt;
carries &amp;lt;code&amp;gt;filled_quantity&amp;lt;/code&amp;gt; and appears on no edge at all. Numbering it before&lt;br /&gt;
its specializations guarantees the accounting applies before any specialized&lt;br /&gt;
event transitions.&lt;br /&gt;
&lt;br /&gt;
For composite orders, a child's completion is observed as its orders&lt;br /&gt;
publisher closing: &amp;lt;code&amp;gt;E&amp;lt;n&amp;gt; - child.orders.close()&amp;lt;/code&amp;gt; for normal completion and&lt;br /&gt;
&amp;lt;code&amp;gt;E&amp;lt;n&amp;gt; - child.orders.close(failure)&amp;lt;/code&amp;gt; for a failure — the failure travels in&lt;br /&gt;
the completion channel, and the parent never inspects the child's report&lt;br /&gt;
statuses to detect it.&lt;br /&gt;
&lt;br /&gt;
Typical events include an order publishing an execution report, a timer&lt;br /&gt;
expiring, and a queue publishing a value:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
order.execution_report(report)&lt;br /&gt;
timer.expired(state)&lt;br /&gt;
queue.pop(value)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Conditions ==&lt;br /&gt;
&lt;br /&gt;
Conditions are numbered formulas over machine variables:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
C0 - filled_quantity == order_fields.quantity&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
They are evaluated synchronously, in label order, immediately after a&lt;br /&gt;
state's action; the first condition that holds performs the transition, and&lt;br /&gt;
if none does the machine waits in the state for events. In-state ordering is&lt;br /&gt;
load-bearing — list the condition that must win first. When a state's&lt;br /&gt;
conditions are exhaustive, the final branch is written as &amp;amp;#949; rather than&lt;br /&gt;
a vacuous always-true condition.&lt;br /&gt;
&lt;br /&gt;
C1 shows a directional formula: the peg cancels and resubmits only when the&lt;br /&gt;
resting price is more passive than the peg formula — the market moved away.&lt;br /&gt;
A symmetric &amp;lt;code&amp;gt;!=&amp;lt;/code&amp;gt; would make the order retreat ahead of an approaching&lt;br /&gt;
market and never fill.&lt;br /&gt;
&lt;br /&gt;
== Execution semantics ==&lt;br /&gt;
&lt;br /&gt;
These rules define precisely how a specification runs.&lt;br /&gt;
&lt;br /&gt;
# '''Entering a state:''' carry out its action, then evaluate its outgoing conditions in label order. The first true condition transitions. If none holds and the state has an &amp;amp;#949; edge, the &amp;amp;#949; transition fires. Only when there is no true condition and no &amp;amp;#949; edge does the machine wait in the state for events.&lt;br /&gt;
# '''Handling an event:''' when an event happens, its body executes. Any refined events derived from it also have their bodies execute afterwards in label order. This is how E0 keeps &amp;lt;code&amp;gt;peg_price&amp;lt;/code&amp;gt; current in every state, and how partial fills accumulate while resting.&lt;br /&gt;
# '''Transitioning on an event:''' at most one transition is performed per event by the first event in label order that happened and labels an out-edge of the current state. If no such event exists, the machine remains in its state.&lt;br /&gt;
# '''Sequencing:''' bodies execute first (in label order), then the transition, then the target state's action, then its conditions.&lt;br /&gt;
# '''Label order resolves guard overlaps.''' A REJECTED report is also terminal; E2 before E4 routes it to the rejected terminal from S2, while in S7 — which has no E2 edge — the transition is E4's and the machine ends normally, since there is nothing left to cancel.&lt;br /&gt;
&lt;br /&gt;
Rule 5's S7 behavior is a general principle: '''any terminal during a user-initiated cancellation is an orderly ending.''' The machine was asked to stop; however the working order resolved, it stopped.&lt;br /&gt;
&lt;br /&gt;
== Recurring patterns ==&lt;br /&gt;
&lt;br /&gt;
* '''The evaluator hub.''' One state (S1) owns the done-or-continue decision, and every path that might end or continue funnels through it: the first quote, an order's terminal report, a reprice-cancel completing. Without it, each of those sources duplicates the check. Hubs also erase cancel ''intent'': a reprice cancel and any other cancel can share one cancelling state when the hub derives the follow-up from the machine's variables rather than from remembering why the cancel was issued.&lt;br /&gt;
* '''The pending-acceptance wait.''' An order in PENDING_NEW cannot be cancelled. A cancel arriving while a submission awaits acceptance parks in an actionless wait state (S7): NEW proceeds to the cancel (S8), any terminal means there is nothing to cancel.&lt;br /&gt;
* '''The cancellation lanes.''' From a state with no live order, &amp;lt;code&amp;gt;cancel()&amp;lt;/code&amp;gt; goes directly to the blue terminal. From a state with a live order, it goes to a cancelling state whose action cancels the order and whose only exits are terminal reports. Re-entering a cancelling state on a second cancel is harmless; a fill winning the race against the cancel still ends the machine normally.&lt;br /&gt;
* '''The persistent-order loop.''' S4 → S1 → S2 resubmits the remainder after any terminal that isn't the machine's own ending — venue cancellations don't kill the order, they re-peg it.&lt;/div&gt;</summary>
		<author><name>Kamal</name></author>
		
	</entry>
</feed>