Filters
Filters are the Trade Director’s mechanism for portfolio-level risk management. They evaluate trade requests and can approve, reject, or modify orders before execution.
Filter types
Individual filters evaluate one order at a time. Examples include weekday blocking and VAR limits.
Batch filters evaluate all pending orders for an epoch simultaneously. This allows ranking and selection, such as approving only the top N signals. Batch filters run after individual filters.
Specifying filters
In configuration files
{
"filters": [
{ "name": "weekday", "blocked-days": ["Friday"] },
{ "name": "max-positions", "max-long-positions": 5, "max-short-positions": 5 }
]
}On the command line
algo run --strategy kbt --symbols @ES,@NQ --filter max-positions \
--filter-inputs 'max-long-positions=3,max-short-positions=3'The CLI --filter flag replaces any filters defined in the config file. Array values use semicolons: blocked-days=Friday;Monday.
Per-strategy filters
Runs can define their own filter chains that execute after global filters:
{
"filters": [
{ "name": "max-positions", "max-long-positions": 10 }
],
"runs": [
{
"strategy": "goldencross",
"filters": [
{ "name": "weekday", "blocked-days": ["Monday", "Friday"] }
],
"symbol": "@ES"
}
]
}Built-in filters reference
nil
Passthrough filter that approves all trades. Useful for baseline comparisons.
weekday
Blocks entries on specified days of the week.
| Parameter | Type | Default | Description |
|---|---|---|---|
blocked-days | array | ["Friday"] | Days to block (full or abbreviated names) |
block-friday | bool | true | Convenience toggle for Friday |
Day names can be full (“Friday”) or abbreviated (“Fri”, “fri”).
Note: For daily bars, the filter checks the fill day (next trading day after signal), not the signal day.
max-positions
Limits the number of concurrent open positions across the portfolio, with separate limits for long and short positions.
| Parameter | Default | Description |
|---|---|---|
max-long-positions | 1 | Maximum long positions (-1 = unlimited) |
max-short-positions | 1 | Maximum short positions (-1 = unlimited) |
Only blocks entry orders; exits always pass through. The filter counts positions by direction across all strategies in the portfolio.
Example use cases:
// Long-only portfolio: allow longs but block all shorts
{"name": "max-positions", "max-long-positions": 5, "max-short-positions": 0}
// Balanced exposure: allow equal long and short positions
{"name": "max-positions", "max-long-positions": 3, "max-short-positions": 3}
// Unlimited shorts, limited longs
{"name": "max-positions", "max-long-positions": 2, "max-short-positions": -1}var
Value at Risk filter. Rejects trades that would exceed portfolio VAR limits.
| Parameter | Default | Description |
|---|---|---|
max-var | 100000 | Maximum total portfolio VAR |
max-var-per-trade | 10000 | Maximum VAR per single trade |
default-stop-pct | 0.02 | Default stop loss percentage for VAR calculation |
risk-budget
Sets position size so each trade’s notional exposure is a fixed fraction of portfolio equity. The filter overrides the quantity set by the strategy’s Buy/Sell verbs.
| Parameter | Default | Description |
|---|---|---|
max-trade-risk-pct | 0.10 | Maximum percentage of equity as notional exposure per trade |
How it works:
- Calculates portfolio equity:
initial capital + closed profit + open profit - Calculates allowed notional:
equity x max-trade-risk-pct - Calculates notional per contract:
price x point value - Sets the trade quantity to
floor(allowed notional / notional per contract) - Rejects the trade if the budget cannot afford even 1 contract
Key behavior: If the strategy requests 1 contract but the budget allows 4, the quantity is increased to 4. If the strategy requests 10 but the budget allows 4, it is reduced to 4. The risk budget – not the strategy – is the authority on position sizing.
Example: With $1M capital and max-trade-risk-pct: 0.10 (10%): allowed notional = $100,000. Corn (@ZC at 450, point value 50): notional per contract = 450 x 50 = $22,500. Filter sets quantity to 4 contracts (100000 / 22500 = 4).
Note: Requires sequential mode (reads portfolio-level state).
atr-position-size
Sizes positions using the Turtle Trading N-units approach. Computes ATR (Average True Range) from recent bar data and sizes each trade so that the dollar volatility per position equals a fixed percentage of portfolio equity. Volatile instruments get fewer contracts; calm ones get more.
| Parameter | Default | Description |
|---|---|---|
risk-pct | 0.01 | Percentage of equity to risk per trade (1%) |
atr-bars | 14 | Number of bars for ATR lookback |
How it works:
- Computes ATR using Turtle-style exponential smoothing:
N = ((period-1)*N + TR) / period - Calculates portfolio equity:
initial capital + closed profit + open profit - Calculates dollar volatility per contract:
ATR x point value - Sets the trade quantity to
floor(equity x risk-pct / dollar volatility), minimum 1
Example: With $1M capital and risk-pct: 0.01 (1%): risk budget = $10,000.
- E-mini S&P (@ES, ATR=50, PV=50): dollar vol = 50 x 50 = $2,500 -> 4 contracts
- Heating Oil (@HO, ATR=0.08, PV=42000): dollar vol = 0.08 x 42000 = $3,360 -> 2 contracts
- Corn (@ZC, ATR=10, PV=50): dollar vol = 10 x 50 = $500 -> 20 contracts
Note: Requires sequential mode (reads portfolio-level state and strategy bar data).
position-size
Adjusts position sizes based on various modes.
| Parameter | Default | Description |
|---|---|---|
mode | "fixed" | "fixed", "risk-parity", or "equal-weight" |
risk-per-trade | 0.01 | Risk per trade as fraction of capital |
max-position-pct | 0.1 | Maximum position as fraction of capital |
asset-class
Limits exposure by asset class.
| Parameter | Description |
|---|---|
asset-class-limits | Maximum exposure value per asset class |
asset-class-max-positions | Maximum position count per asset class |
category
Limits exposure by futures category (energy, metal, index, etc.).
| Parameter | Description |
|---|---|
category-limits | Maximum exposure value per category |
category-max-positions | Maximum position count per category |
volatility
Rejects trades when recent realised volatility exceeds a threshold.
| Parameter | Default | Description |
|---|---|---|
lookback-bars | 20 | Volatility calculation period |
max-threshold | 0.03 | Maximum volatility threshold |
top-n
Batch filter that approves only the top N entry orders by signal strength (TDValue). Exit orders are always approved unless position continuity suppresses them.
| Parameter | Default | Description |
|---|---|---|
max-orders | 4 | Maximum entry orders to approve per epoch |
preserve-positions | true | Suppress unnecessary exit-and-re-entry of existing positions |
Behavior:
- Sorts entry orders by TDValue descending (higher = stronger signal)
- Approves top N entries, rejects the rest
- Orders without TDValue are treated as TDValue=0
- Ties are broken by strategy ID alphabetically for determinism
Position continuity: When preserve-positions is enabled (default), the filter prevents unnecessary exit-and-re-entry of existing positions. If a strategy has an existing position and wants to continue it (entry in same direction), and the continuation entry ranks in the top-n, the paired exit order is suppressed. This reduces slippage and commission costs.
top-n-long-short
Like top-n, but ranks long and short orders separately.
| Parameter | Default | Description |
|---|---|---|
max-longs | 4 | Maximum long (Buy) orders to approve per epoch |
max-shorts | 4 | Maximum short (Sell) orders to approve per epoch |
max-orders | Legacy: sets both max-longs and max-shorts | |
preserve-positions | true | Suppress unnecessary round-trips |
Long and short orders are ranked by TDValue independently. Long continuations compete in the longs pool only; short continuations compete in the shorts pool only. Position continuity works the same as top-n.
The audit trail
Generate a JSON audit trail of every filter decision:
algo run --config portfolio.json --audit-trail audit.jsonEach entry records the timestamp, strategy, symbol, order type, price, whether it was approved, which filter processed it, and the rejection reason (if any).