SJS state
Store bounded strategy memory and separate observations, attempts, and confirmed outcomes.
Use a named object beneath context.sjsState for the mutable state your strategy owns. The quickstart uses crossoverTutorial so its fields do not collide with other code using the same root.
The tutorial state
| Field | Meaning |
|---|---|
closes | At most three accepted observations |
lastFrame | Latest consumed market timestamp |
previousRelation | Prior sign of close minus average |
signal | Current computed decision and its evidence |
busy | An async evaluation is in progress |
lastAttempt | Most recent submission attempt and its outcome |
lastError | Most recently caught exception, if any |
signal is cleared for a newly accepted timestamp before warmup. lastAttempt remains until another submission attempt replaces it, and lastError remains until your code changes or clears it. Always inspect their associated frame rather than assuming every field describes the current invocation.
Three different protections
The sample has separate guards because they answer different questions:
busyprevents overlapping asynchronous evaluations in this strategy.lastFrameprevents repeated or older observations from producing another attempt.getOrders()checks whether the session currently reports outstanding orders.
An in-flight JavaScript call and a pending broker order are different states. Clearing busy after an API call does not mean the order has filled. A timestamp guard also cannot reconcile an order after a session restart.
Attempts consume the signal
The quickstart advances its signal relation before reading execution state. If it encounters pending orders or the request fails, the crossover has still been observed. The same timestamp is not retried, and a later bar remaining on the same side is not a new crossover.
This is a deliberate teaching rule. If you want retries, define their market-time deadline, maximum attempts, pending-order reconciliation, and conditions that invalidate the original signal. Simply deleting lastFrame on failure can create repeated submissions.
State lifetime
Treat sjsState as session memory. This example does not persist it across restarts, migrate it when settings change, or reconstruct it from broker history. Agent Lab source revisions preserve the experiment's code; a fresh run builds its own runtime memory.
Restart the historical run when changing period, quantity, or sampling rules. Keeping old closes while changing their meaning makes comparisons difficult to explain.
Keep memory bounded
Prefer a small numeric window, IDs, timestamps, and compact decision summaries. Avoid appending every frame forever or storing full session objects, functions, and promises in state. Read execution truth through the session APIs instead of maintaining a second guessed position ledger.
Continue with Orders and positions and Examples.