Custom Guards
Guards are pre-execution safety checks that run before orders reach the broker. You can create custom guards to enforce any risk rule you need.
OperationGuard Interface
interface OperationGuard {
readonly name: string
check(ctx: GuardContext): Promise<string | null> | string | null
}
Return null to allow the operation, or a string to reject it with a reason.
GuardContext
The context provides everything you need to evaluate an operation:
interface GuardContext {
readonly operation: Operation // The operation being checked
readonly positions: Position[] // Current portfolio positions
readonly account: AccountInfo // Account info (equity, cash, etc.)
}
The guard pipeline fetches positions and account info from the broker before running guards, so your guard always has fresh data.
Creating a Custom Guard
1. Implement the Guard
import type { OperationGuard, GuardContext } from './types.js'
export class MaxDailyTradesGuard implements OperationGuard {
readonly name = 'max-daily-trades'
private maxTrades: number
private todayCount = 0
private lastResetDate = ''
constructor(options: Record<string, unknown>) {
this.maxTrades = Number(options.maxTrades ?? 10)
}
check(ctx: GuardContext): string | null {
if (ctx.operation.action !== 'placeOrder') return null
const today = new Date().toISOString().slice(0, 10)
if (today !== this.lastResetDate) {
this.todayCount = 0
this.lastResetDate = today
}
this.todayCount++
if (this.todayCount > this.maxTrades) {
return `Daily trade limit reached (${this.maxTrades} trades per day)`
}
return null
}
}
2. Register in the Guard Registry
import { registerGuard } from './registry.js'
import { MaxDailyTradesGuard } from './max-daily-trades.js'
registerGuard({
type: 'max-daily-trades',
create: (opts) => new MaxDailyTradesGuard(opts),
})
3. Configure per Account
{
"id": "alpaca-paper",
"type": "alpaca",
"guards": [
{ "type": "max-daily-trades", "options": { "maxTrades": 5 } }
]
}
Guard Pipeline Flow
Guards run sequentially during the push step:
- Pipeline fetches positions + account info from broker
- For each staged operation, builds a
GuardContext - Runs each guard in order — first rejection wins
- If all guards pass, the operation is dispatched to the broker
- If any guard rejects, the operation fails and the rejection is recorded
Built-in Guards for Reference
| Guard | What it checks |
|---|---|
max-position-size | Projected position value vs equity percentage |
cooldown | Minimum time between trades on the same symbol |
symbol-whitelist | Whether the symbol is in an allowed list |
These are defined in src/domain/trading/guards/ and serve as reference implementations.