Policy examples
Extend the quantity tutorial with a frame-driven exit and a repeatable validation sequence.
These examples build on the quickstart's complete L3 script. Add policy objects to that script and include them in its policies array; retain the existing frame and intent wiring.
Close a long position below a configured price
This factory reads the first current position and proposes a close when a long position's current bar close is at or below a configured price. It does not create an entry or install a broker-native stop order.
function createLongPriceExit({ id, exitBelow }) {
if (typeof id !== "string" || !id.trim()) {
throw new Error("A policy ID is required");
}
if (!Number.isFinite(exitBelow)) {
throw new Error("exitBelow must be a finite number");
}
return {
id,
description: "Close a long position at or below a configured bar-close threshold",
intents: ["close_position"],
async onFrame({ policyId, context, bar }) {
const positions = await context.getPositions();
const position = positions[0];
if (!position || position.action !== "BUY") {
return { policyId, status: "idle", reason: "No long position" };
}
if (typeof bar?.close !== "number" || !Number.isFinite(bar.close)) {
return { policyId, status: "waiting", reason: "Missing finite bar close" };
}
if (bar.close > exitBelow) {
return { policyId, status: "watching", reason: "Price above exit threshold" };
}
const reason = `Bar close ${bar.close} reached exit threshold ${exitBelow}`;
return {
policyId,
status: "triggered",
reason,
intent: {
kind: "close_position",
position,
priority: 20,
reason,
metadata: { barClose: bar.close, exitBelow },
},
};
},
};
}After defining the factory, replace the quickstart's policy array with this composition. The price 100 is an illustrative threshold; choose a meaningful value for your test instrument.
const policies = [
quantityPolicy,
createLongPriceExit({ id: "long-price-exit", exitBelow: 100 }),
];If you already replaced quantityPolicy with the reusable factory, use createQuantityPolicy({ id: "quantity-limit", maximumQuantity: 2 }) in its place.
The quantity policy returns an observation-only frame result, so the exit can win when triggered. The exit has no onIntent; the quantity policy continues to check ordinary automated place/modify requests.
Expected behavior
| Session state | Price | Exit result |
|---|---|---|
| Flat or first position is short | Any | idle, no action |
| Long position | Missing or invalid | waiting, no action |
| Long position | Above threshold | watching, no action |
| Long position | At or below threshold | triggered, proposes close_position |
The executor submits the selected close using source l3. Automation controls can still reject it. A successful submission does not establish its fill price, and this bar-close condition does not inspect intrabar highs or lows.
The condition can propose a close on successive frames until the position changes. The shared close executor supplies ignoreDuplicate: true, but this example does not implement a general pending-order tracker or a retry schedule. Add explicit lifecycle rules before adapting it to more complex execution behavior.
Validate the combined stack
Start with the quantity policy alone and confirm its checks. Then add the exit policy and compare a flat frame, a long position above the threshold, and a long position at the threshold. Finally, inspect both an accepted close request and a rejected one.
The important observations are the selected policy ID, the proposal's reason, lastExecution.executed, and the resulting position/trade state. Keep the instrument, data window, and execution settings fixed while comparing source revisions.
For more variations, see the module factory, helper observer, and frame observer.