Portfolios
Run several assets from one shared capital pool: rosters, contention, rebalancing, risk controls, deposits and withdrawals, the book report, and the known limits behind a surprising zero.
A portfolio runs several assets from one shared pool of capital as a single book. Each asset carries its own strategy, and a contention rule decides who gets the cash when more than one asset wants to enter on the same bar. Where a single backtest is one symbol, one strategy and its own balance, a portfolio is a roster of assets competing for one pool, with a shared equity curve, per-asset attribution, and a record of every signal that could not be filled.
The v1 portfolio path is spot crypto only. Symbols resolve to Binance USDT pairs (BTC becomes BTCUSDT). A book holds up to 20 assets.
Building a book#
There are three ways in, and they all land on the same review then run flow. For a step by step walkthrough see Build a portfolio.
| Path | How |
|---|---|
| One prompt | Describe every asset in a single sentence. A multi-asset prompt is split into one parsed strategy per asset, and the terminal switches to Portfolio mode for you. |
| Visual builder | Flip the sidebar toggle from Single asset to Portfolio, open the Visual tab, and add assets by hand. Each asset opens the normal strategy builder canvas. |
| API / MCP | parse_portfolio then run_portfolio, see the MCP server. |
Buy BTCUSDT and ETHUSDT on 1d when price closes above the 20 EMA; exit when it closes below. $50k, prorata.Each asset shows up as a review card with the clause the splitter assigned to it ("Split as"), its parsed logic, and its market. Before you run, you can edit any asset, copy one asset's rules to the whole roster, or remove it.
Shared capital & contention#
A book trades from one initial capital amount, not one balance per asset. The terminal starts a new book at $30,000; the API and MCP default to $100,000.
When two or more assets signal an entry on the same bar and there is not enough free cash for all of them, the contention rule resolves the conflict:
| Rule | Behaviour |
|---|---|
rank (default) | Priority order wins. Priority follows roster order unless you set it, say "prioritise BTC over ETH" to pin it. |
prorata | The contested cash is split proportionally across the competing assets. |
strength | The strongest signal fills first. Ask for it with "strongest signal fills first". |
A signal that cannot be funded is never dropped silently. It is written to the skip ledger with a reason, see Reading the book report.
Rebalancing#
Off by default. You turn it on by naming target weights; percentage stops and take-profits are never mistaken for weights.
| Setting | Values |
|---|---|
| Schedule | daily, weekly, monthly, quarterly, yearly, or never (default monthly) |
| Overlay mode | resizes open positions toward the target weights on each boundary, while entry signals still fire |
| Allocation mode | holds the whole book to the target weights with no entry signals, a long-only hold-to-weight book |
| Targets | per-asset weights that sum to 100% or less; a cash remainder is allowed |
| Cash sleeve | state the remainder explicitly: "hold 40% BTC, 30% ETH and 30% cash" |
| Drift band | rebalance off-calendar whenever a sleeve drifts this far from target. Quote it in percentage points or in percent: a band is a difference between two percentages, so "5 points off target" and "a 5% drift band" are the same rule |
| Minimum trade | skip corrective trades below a dollar amount, so a tight band stops generating $12 trades |
Rebalance monthly to 40% BTC, 30% ETH, 30% SOL.
Hold 60% BTCUSDT and 40% ETHUSDT, rebalance quarterly.
Allocate 50% BTCUSDT and 50% ETHUSDT once and never rebalance.
Hold 60% BTCUSDT and 40% ETHUSDT, rebalance monthly but skip rebalance trades under $500.
Equal weight BTCUSDT, ETHUSDT and SOLUSDT, rebalance monthly and also whenever a position drifts more than 5 points from its target.Every rebalance is turnover you pay for. Quarterly tracks the targets more loosely than monthly and trades about a third as often; never allocates once on the first bar and then lets the winners run, which is the buy-and-hold baseline every other schedule should be measured against.
Rotation#
Instead of fixed weights, rank the universe each rebalance and hold only the leaders, or the laggards.
Buy the top 3 by momentum over 30 days, rebalance weekly.
Hold the 2 lowest volatility coins, rebalance monthly.
Rotate into the best 2 by 30-day return and short the worst 2.Rank by momentum, return, rate of change, RSI, Sharpe or volatility. A rotation is always an allocation book, the held set and the weights are both chosen by rank, so entry signals do not run.
Ranking is relative: it answers which of these is best, never is any of them any good. In a market where everything is falling, a top-N rotation dutifully buys the best of the losers and stays fully invested all the way down. Add the absolute test and the book goes to cash instead when nothing clears it:
Hold the top 2 by 90 day momentum but only while their momentum is positive, rebalance monthly.
Rotate into the top 3 by momentum, cash if none are positive.That second test is what makes it dual momentum. The floor gates the LONG side only: it says which names are worth owning, not which are worth shorting, so a long/short rotation still takes its short sleeve from the worst-ranked names. When nothing clears the floor the run says so explicitly, so a book that deliberately sat out a bear market is never mistaken for a broken rotation.
Risk controls#
All optional, and all off unless you ask for them. State them once, anywhere in the prompt, a control that talks about the whole book never has to be repeated per asset. Percentages that belong to a leg ("stop loss 3%") are never mistaken for a book knob.
Every control below can be typed in the prompt or edited in the Book settings panel on the review page: including the ones whose value is a ladder, a schedule, a list of days or a session window. Clearing an editor turns that family off completely; there is no "set but inert" state. An edit you make by hand is authoritative: re-parsing the prompt afterwards will not overwrite it.
Size and concentration#
| Control | Effect | Say |
|---|---|---|
| Book drawdown stop | Flatten the book and halt new entries once equity falls this far below its peak. Deliberately permanent for the rest of the run | "stop the book at 20%" |
| Book take-profit | The profit-side twin: close everything and stop once the account is up past a target | "close the whole book at +30%" |
| Max concurrent positions | Cap how many assets can be open at once | "at most 3 open positions" |
| Total exposure cap | Ceiling on total open notional versus book equity | "cap exposure at 80% total" |
| Per-asset cap | Largest share of the book any one asset can hold | "max 20% per asset" |
| Per-sleeve loss stop | Force-close any position that falls this far below its own entry, whatever the strategy's own exit says. It can re-enter later | "stop each position that falls 12% below its entry" |
| Sector exposure cap | Bound a whole group's share of the book, by naming the group's members | "cap memecoins (DOGEUSDT, SHIBUSDT) at 15%" |
| Positions per sector | Bound how many names a group may hold at once | "max 2 positions per sector" |
| Cash reserve | Never spend the book below this much cash | "always keep 20% in cash" |
| Correlation cap | Skip a new entry whose recent returns move too closely with a position already open | "avoid holding two positions correlated above 0.8" |
| Volatility target | Scale every new position so the book's realized volatility tracks a target | "target 15% annualized volatility" |
| Sleeve drawdown halt | Retire an asset for the rest of the run once its own running P&L is this far below its peak | "stop trading any coin that is down 20%" |
| Drawdown-scaled sizing | Make every new position smaller while the book is in a drawdown, restoring full size on recovery | "halve position size while the book is in a 10% drawdown" |
| Open risk budget | Bound the total distance to stop × size across every open position, ten positions each risking 1% is a 10% book risk | "never have more than 6% of the book at risk" |
| Correlation clusters | Group everything that moves together and hold at most N names per cluster, the transitive answer where the correlation cap is the pairwise one | "at most one position per correlation cluster" |
| Positions per symbol | On a book with several strategies on the same coin, bound how many of them may be open at once | "only one position per symbol", "BTC gets 3 positions max" |
| Sectors by name | Tag your sectors in plain English once, then cap them by name | "BTC and ETH are majors, DOGE and SHIB are memecoins. Max 60% in majors, at most 2 memecoins" |
| Minimum position size | Skip a fill that clears the venue minimum but is too small to be worth the commission | "skip any position under $1000" |
| One sizing rule | Replace whatever the individual legs said with a single book-wide rule | "risk 1% of the book per trade", "size every position at 10% of equity" |
| Per-position volatility target | Even out the sleeves so a wild coin and a calm one carry comparable risk | "target 20% volatility per position" |
| Top-N concentration cap | Bound what the biggest holdings add up to together. Four names at 24% each break this while breaking no single-name limit | "cap the top 3 names at 60% of equity", "no more than 60% of the book in the top 3 holdings" |
| Every sector, one number | Bound each sector without enumerating them, the way a mandate is written. Untagged sleeves are never capped by it | "never put more than half the book in one sector", "max 40% per sector" |
| Liquidity floor | Skip an entry on any bar whose traded value was below a dollar figure, so the book never fills where the tape could not have | "require at least $10m of daily volume", "skip anything trading under $1m" |
The per-sleeve loss stop asks is this open position underwater, it closes anything trading below its own entry. The sleeve drawdown halt asks has this sleeve given back its gains, a coin that ran up 30% and round-tripped the lot never triggers the first one, because no single position was ever deeply below entry. Use the loss stop as a safety net on a leg with no stop of its own; use the drawdown halt to stop feeding an asset that has stopped working.
"Cap memecoins at 15%" and "max 2 positions per sector" are not the same instruction. One oversized name can consume a whole notional cap on its own, while two small ones fit comfortably inside it. Use the notional cap to bound risk, the count cap to force breadth.
Timing and pacing#
| Control | Effect | Say |
|---|---|---|
| New positions per period | However many signals fire, only this many new positions open per bar, day, week or month. The strongest get the slots | "open at most 1 new position per day" |
| Daily / weekly loss limit | Stop opening for the rest of the period once the book is down this much from the period's opening equity, in percent or in dollars, then resume next period | "stop trading for the day after a 3% loss", "cap the daily loss at $500" |
| Quit while ahead | The profit mirror: stop opening once the period has banked its target. Entry-side: open positions keep managing themselves, so the banked number can still drift | "stop for the day after making $1000", "done for the day once we're up 2%" |
| Re-entry cooldown | After a sleeve closes, that asset waits this many of its own bars before it can be bought again | "no re-entry for 5 bars" |
| Minimum holding period | A signal exit cannot close a position before it has been held this many bars | "hold every position for at least 5 bars" |
| Scheduled flatten | Close every open position on a calendar boundary, then carry on trading | "close everything on Friday", "no positions over the weekend" |
| Trading calendar | Only open positions on these days or months | "only trade Monday to Thursday", "don't trade in December" |
| Quarters | Only open in certain quarters | "only trade in Q4" |
| Days of the month | Sit out particular dates, or the days around month end | "never trade on the 1st", "no trading around month end" |
| Trading hours | Only open inside a UTC window, the intraday version of the calendar | "only trade between 08:00 and 16:00 UTC" |
| One-off blackouts | Sit out a named date range | "don't trade between 2024-12-20 and 2025-01-05" |
| Book warm-up | Let the book settle before it commits capital | "wait 50 bars before trading", "skip the first month" |
| Total trade budget | A hard ceiling on trades for the whole run | "no more than 100 trades in total" |
| Trades per period | The same ceiling, per day, week or month | "no more than 5 trades a week" |
| Entry spacing | Require a gap between any two entries, so the book cannot go on all at once | "leave at least 3 bars between any two entries" |
| Turnover cap | Bound annualized turnover as a share of equity, counting both sides of a round trip | "keep annual turnover under 200%" |
| Breadth confirmation | Fund nothing unless this many sleeves signal on the same bar | "only enter when at least 2 assets signal together" |
"At most 3 open positions" bounds how much the book holds. "One new position a day" bounds how fast it gets there, a book allowed three names can still be told to leg into them one per day. And the daily loss limit is the recoverable twin of the book drawdown stop: that one halts for the rest of the run, this one sits out the session and starts again tomorrow.
Direction and regime#
| Control | Effect | Say |
|---|---|---|
| Gross long / short caps | Bound each side of a long/short book separately | "max 60% long and max 40% short" |
| Positions per direction | Bound how many names each side may hold at once | "at most 2 shorts at a time" |
| Net exposure cap | Bound how far the book may lean either way | "keep net exposure under 20%", or "run it market-neutral" |
| Equity-curve filter | While book equity is below the average of its own recent equity, stop opening, or go fully to cash, and resume when the curve recovers above it | "stop trading when the equity curve drops below its 50-day average" |
| Market regime gate | Nothing in the book opens unless a condition on another symbol holds | "only trade when BTC is above its 200 day EMA" |
A total exposure cap cannot express a market-neutral book: it reads a 100%-long book and a 50/50 long-short book as the same number. The net cap is judged across the whole bar, so a hedged pair is never refused for being "too long" on its first leg, only a lopsided bar loses its weakest name on the heavy side.
It defers signal exits only, the indicator or comparison rule you wrote as your exit. A stop loss, take profit, trailing stop, liquidation and an explicit "exit after N bars" always fire, at any age. A minimum hold that swallowed a stop would be a risk, not a risk control.
Closing out every Friday removes weekend gap risk and removes every trend that would have run straight through the flush. It is a real trade-off, not a free safety measure, run the same book with and without it before keeping it.
It damps long drawdowns, and it also guarantees you miss the first leg of every recovery, the curve has to climb back above its own average before the book will trade again. Judge it on the whole equity curve, not on max drawdown alone.
The regime gate can also act rather than merely pause, and it speaks plain words:
| Instruction | Effect |
|---|---|
| "go to cash when BTC drops below its 200 day MA" | A regime break CLOSES what the book holds, not just new entries; positions re-open on their own signals when the regime returns |
| "go to cash in a bear market" | Read as BTC below its 200-day SMA, flattening, both halves flagged as inferred |
| "avoid trading when BTC is dropping fast" | BTC's 7-day rate of change staying above −10%, flagged as inferred |
| "only trade alts when BTC is stable" | BTC's NATR(14) below 5%, flagged as inferred |
| "go to cash when @regime-score drops below 40" | Your own uploaded series decides risk-on or risk-off for the whole book |
A daily regime filter on a 4h book reads the last completed daily bar, so there is no look-ahead. During the warm-up, before the regime series has enough history, the gate is closed a filter that has not resolved yet is not permission to trade.
Entry filters on each leg's own chart#
The regime gate above judges ONE symbol for the whole book. These ask the same question of EVERY chart separately, which is the shape most trader filters take: "only trade when ADX is above 25" means each leg's own ADX.
| Control | Effect | Say |
|---|---|---|
| Indicator filter | Entries only while an indicator condition holds on that chart | "only trade when ADX is above 25", "only enter when RSI is below 70" |
| Custom filter | The same gate driven by your own uploaded indicator | "only trade when @trend-score is above 70" |
| Price vs its own average | The per-chart trend filter | "only go long when price is above the 200 day moving average" |
| Trend alignment | Longs above the average, shorts below it: one instruction, two mirrored conditions | "only trade in the direction of the 200 EMA" |
| Chop avoidance | ADX above 20, the conventional trend floor, flagged as inferred | "avoid choppy markets" |
The signal still fires and lands in the skip ledger with its own reason, so the report shows how much the filter discarded. An open position always exits by its own rules, even when the filter has turned against it. And the filter fails CLOSED, a condition that cannot be evaluated blocks the entry rather than silently opening the book.
Sessions and day boundaries#
| Control | Effect | Say |
|---|---|---|
| Named sessions | An approximate UTC window (London 08:00-16:00, New York 13:00-21:00, Tokyo 00:00-08:00) flagged as inferred | "only trade the London session" |
| Start every day flat | Flush the book on each UTC day's last bar; re-enter on signals next morning | "start every day flat", "don't hold overnight" |
| Avoid the open / close | The crypto day opens at 00:00 UTC | "avoid the first hour after the open", "no new positions in the last hour" |
| First hours only | The mirror: only the day's opening burst | "only trade the first two hours" |
Costs and execution#
A backtest with no fees and instant fills is a brochure, not a result. Both are stated once for the whole book.
| Control | Effect | Say |
|---|---|---|
| Fees and slippage | Apply the same cost assumption to every leg | "use 0.1% fees and 0.05% slippage across the book" |
| Fill delay | Model the gap between a signal and a fill by entering N bars later | "assume a 1 bar delay on every entry" |
| Next-open fills | Fill every entry at the NEXT bar's open instead of the signal bar's close, removing a free fill at the very price the signal was computed from | "assume I get filled at the open of the following day" |
| Short borrow | Charge the annual borrow rate as a per-bar carry on every open short. Name the book's timeframe: an annual rate cannot be converted without it, and the run declines rather than guessing | "shorts cost 6% a year to borrow" |
| Cash yield | Credit interest on idle cash, so a defensive book is not silently punished for being defensive | "earn 5% on the cash we are not using" |
A signal computed at a bar's close cannot also be filled at that close: next-open fills remove that free fill, and they will make almost any result worse. That is the point. The mirror case is cash yield: with idle cash earning exactly 0%, a book that correctly sat out a bad quarter still loses to a benchmark that compounded through it, and the comparison says nothing about the strategy. Turn both on before you compare two books.
A frictionless run draws an honesty flag for a reason: at a few trades a month it barely matters, and at several a day it is the difference between a strategy and a fee generator. Run your real cost assumption before you believe a high-turnover book.
One stop and one target for every position#
The most-used trader instruction of all, stated once for the whole book instead of repeated on every leg. An exit is added only where the leg does not already carry one of that type, the leg's own rule wins, and the result reports which legs took the book's exits and which kept their own.
| Control | Effect | Say |
|---|---|---|
| Book stop | A percentage stop on every position | "put a 2% stop on every position" |
| Book target | A percentage target on every position | "take profit at 8% on every trade" |
| Book trailing stop | Trail each position behind its own peak | "trail every position 3%" |
| ATR stop | A volatility-sized stop, a calm coin and a wild one get different distances from the same instruction | "use a 1.5 ATR stop on every position" |
| ATR trail | The trailing version of the same idea | "trail every position with a 2 ATR stop" |
| Indicator exit | Close each position when its own chart says so | "close every position when RSI is above 80" |
| Time stop | Close a position that has run out of time rather than price. Lowest priority: a trade that worked has already left by its own exit | "time stop after 20 bars", "exit if the trade has not worked in 10 bars" |
| Max holding time | The calendar phrasing of the time stop, the ceiling that pairs with the minimum-hold floor | "only hold trades for a maximum of 5 days" |
"Put a 2% stop on every position" is a book instruction; a bare "stop loss 2%" belongs to the leg that says it. That anchoring is what lets both live in one prompt without stealing from each other. An ATR exit on a series where ATR genuinely cannot be computed is refused and reported, never silently added, a stop the engine can never fire would leave the position unprotected while the config says otherwise.
The risk unit#
Traders think in R: what a trade risks, and what it pays if it works.
| Control | Effect | Say |
|---|---|---|
| Reward-to-risk | Every leg with a percentage stop gets a target at stop × R | "risk 1% per trade with a 2 to 1 reward to risk", "target 2R on every trade" |
| Fixed dollar risk | Size so a stop-out costs a stated amount. Does NOT compound, that is the point of stating it in dollars | "size every position so it risks $500" |
| Per-trade ceiling | A cap, not a sizing rule: a leg asking for less keeps its size, one asking for more is trimmed | "never let one trade lose more than 1% of the account" |
| Portfolio heat | The open-risk budget, under the name traders use: distance-to-stop × size summed across open positions | "keep total heat under 6%" |
| Risk in words | Sub-1% risk written the way people write it | "risk half a percent per trade" |
R is a ratio, so it needs a percentage stop to multiply. A leg with an ATR or swing stop, or one that already states its own target, is listed in the result as skipped, "target 2R" that quietly did nothing on half the book is worse than an error.
Scaling out#
| Control | Effect | Say |
|---|---|---|
| Ladder | Take profit in pieces; whatever the rungs don't add up to keeps running | "sell a third at 5%, a third at 10%, and hold the rest" |
| R-denominated rung | A rung at a multiple of the leg's own stop | "scale out half at 2R and let the rest run" |
| Partial then full | Half at the first level, everything left at the second | "partial exit at 5% and full exit at 10%" |
Sizes are shares of the ORIGINAL position: "a third, then a third" really closes a third each time, with the remaining-quantity arithmetic handled for you. Slices appear on the closed trade as partial exits, the same shape the single-asset report already renders.
Pyramiding discipline#
| Control | Effect | Say |
|---|---|---|
| Never average down | Refuse any add while the position is at or below its blended entry | "add to winners only", "never average down" |
| Pyramid cap | Total entries per position, the opener included | "pyramid up to 3 entries" |
| …counted in adds | The same cap stated the way people actually say it. Two adds is three entries, because the opener counts | "add to a winner at most twice" |
A refused add leaves the position exactly as it was and is counted in the result, a disciplined book shows what it declined rather than looking like the adds never fired.
Protecting a profit#
| Control | Effect | Say |
|---|---|---|
| Book profit lock | A ratchet: once the book is up past a trigger, defend a floor. The floor only ever moves up, so a give-back cannot unwind a lock already earned | "once the book is up 20%, never let it go below breakeven" |
| One-number profit lock | The same ratchet with only the trigger stated, the floor is inferred at half the trigger, and the result says so | "lock in profits above 20%", "protect gains once up 15%" |
| Give-back fraction | The book trailing stop as a fraction of the peak | "give back no more than a third of the peak" |
| Book trailing stop | Trail the whole book a fixed distance behind its own peak | "trail the book 10% off its peak" |
| Stop management | Move every position's stop to breakeven, or start trailing it, once it is far enough in profit | "move every stop to breakeven after +5%" |
Sizing that responds to something#
All of these are multipliers on the size you asked for, and they compose: with a ceiling, so several cannot conspire into a position nobody asked for.
| Control | Effect | Say |
|---|---|---|
| Size by recent record | Cut size after a losing streak, press after a winning one | "halve size after two losses", "double size after two winning trades" |
| Ease into full size | Ramp from a starting exposure to full size over a period | "start at 25% exposure and scale to 100% over 3 months" |
| Kelly fraction | Size from the book's own measured edge | "use half Kelly sizing" |
| Market-state rules | A multiplier gated on a condition, judged on each leg's own chart at entry | "halve size when ADX is below 20", "reduce size in high volatility" |
| Custom conviction | The same rule driven by your own uploaded indicator | "double size when @conviction is above 80" |
The Kelly size is computed from the book's own closed trades and stays inert below 20 of them. A bare "Kelly sizing" is read as half Kelly and the result says so, full Kelly is far too volatile to run, and a measured edge that turns out to be negative sizes to zero rather than short.
Circuit breakers#
The count-based cousins of the daily loss limit: that one counts percent lost, these count events.
| Control | Effect | Say |
|---|---|---|
| Losing-streak halt | Stop opening for the rest of the run after a run of losing trades | "stop the book after 5 consecutive losing trades" |
| Losing-streak pause | Stand down for a number of bars, then come back | "pause for 5 bars after 2 losses" |
| Done for the day | Stand down for the rest of the day, week or month | "stop trading for the rest of the day after 2 losses" |
| Losing periods | Stop after a run of losing days, weeks or months | "stop trading after 3 losing days in a row", "halt after 3 losing weeks" |
| Performance floor | Stop once the measured win rate or profit factor falls through a floor | "stop if the win rate drops below 40%" |
| Rolling Sharpe floor | Stop opening while the risk-adjusted record is under a floor, the drawdown-blind twin of an equity-curve filter. State no window and it is read over the last 60 equity points, and the run tells you it assumed that | "stop trading if the rolling sharpe drops below 0.5", "stop opening while our 90-day Sharpe is negative" |
| Behind the benchmark | A RELATIVE drawdown stop: the book can be UP and still trip this, because what it measures is the gap to buy-and-hold | "benchmark against BTCUSDT buy and hold and stop trading if we underperform the benchmark by 15%" |
"Stop trading" means take no new risk. Anything already open still manages itself by its own exits, because closing a book at whatever the market happens to be showing is a different instruction from the one you gave. A performance floor is also inert below 20 closed trades: halting a book on noise is worse than not halting it.
Aiming a control at one coin#
Almost every control above can name a single asset instead of the whole book. The more specific statement always wins.
Cap BTCUSDT at 40% and ETHUSDT at 20%.
Risk 2% on BTCUSDT and 1% on ETHUSDT.
Stop BTCUSDT at 10% and cut ETHUSDT if it's down 8%.
Hold ETHUSDT for at least 5 bars.
Wait 10 bars before re-entering BTCUSDT.Aiming a cap at an asset that is not in the portfolio is refused rather than quietly dropped, a cap that silently is not there reads exactly like a cap that is.
Custom indicators at the book level#
Anywhere a built-in indicator works in a book-level control, a custom (@-named) indicator you
have uploaded works too. The series is loaded per leg through the same resolver a leg's own custom
condition uses, so an unknown name fails the run loudly instead of gating on a column that is not
there.
| Surface | Say |
|---|---|
| Entry gate | "only trade when @trend-score is above 70" |
| Position sizing | "double size when @conviction is above 80" |
| Book regime, and go to cash on it | "go to cash when @regime-score drops below 40" |
| Rotation ranking | "hold the top 2 by @score, rebalance weekly" |
| Position exits | "close everything when @exit-score is above 80" |
An entry filter that cannot be evaluated blocks the entry, a gate that is not there must not silently open the book. A sizing rule that cannot be evaluated leaves the size alone, a rule that is not there must not zero a position the strategy asked for. One is permission, the other is adjustment, and each fails in the direction that does less damage.
Capital deployment and signal selection#
| Instruction | Effect |
|---|---|
| "only take the strongest signal each day" | Strength contention picks WHICH signal wins the capital; a one-per-day allowance decides HOW OFTEN |
| "rank the signals and take the best two" | When five sleeves fire together, the two best-ranked open and the rest are ledgered |
| "trade with 50% of capital and keep the rest in reserve" | The cash-reserve floor, stated from the deployed side |
| "deploy capital gradually over the first month" | An exposure ramp from 0% to 100% of the stated size, flagged as inferred |
| "withdraw profits monthly" | ALL of the gain out each month: flagged as inferred, because it turns off compounding and you should see that stated |
What the book is measured against#
Every portfolio is compared to a benchmark, and by default that is an equal-weight buy-and-hold of the book's own constituents. That is often not the comparison you meant, so you can say which one you want.
| Benchmark | Say |
|---|---|
| Buy and hold one asset, including one the book does not trade | "compare it against BTC buy and hold" |
| A stated mix, bought once and left to drift | "compare to a 60/40 BTC ETH portfolio" |
| The same mix, systematically rebalanced | "compare to a 60/40 BTC ETH portfolio rebalanced monthly" |
| Cash, a flat line at your starting capital | "compare against sitting in cash" |
Whatever you pick becomes the denominator for the numbers that actually answer "was this worth it": outperformance, beta, correlation, annualized alpha, tracking error, information ratio, and up/down capture. Add "assume a 4% risk free rate" and the alpha is measured over that hurdle instead of zero.
Beta against cash is not zero, it is undefined, a flat benchmark has no variance to regress against. The report leaves those fields empty rather than printing a number it cannot justify. Against cash, the outperformance figure is the whole story.
"A 60/40 portfolio" normally means one bought 60/40 and left alone, so that is the default. Saying "rebalanced monthly" makes it systematically rebalanced, which harvests the mean reversion between its legs and is materially harder to beat in a choppy market.
Deposits and withdrawals#
A book can also move money across the account boundary on a schedule: pay in every month, or take a share of the profits out.
| Instruction | Effect |
|---|---|
| "add $1000 to the account every month" | Cash is added to the pool; the strategy then puts it to work on its own signals |
| "withdraw 20% of profits every month" | A share of the gain above the starting capital is taken out |
| "take out $2000 each month" | A fixed amount is taken out |
This is not the same instruction as DCA. Dollar-cost averaging deploys the pool the book already has, buying a fixed amount on a schedule. A deposit grows the pool and leaves the timing to your rules. A withdrawal only ever comes out of cash the book is actually holding, it never sells a position to fund one, and a profit share never touches the starting capital.
Money you paid in raises the final equity without the strategy earning a cent, so a plain "total return %" on a contributed book is not a performance figure. The report carries a contribution-adjusted return measured against the money actually put in, with withdrawals added back, that is the number to judge the book on. It is reported for every cash flow, however you phrased it.
A book may state each cash flow once. "Withdraw $1,000 a month" is one instruction; saying it twice, once in prose and again as an explicit setting, used to make the book pay out twice, so an overlapping pair is now refused by name rather than applied silently. Deposits, fixed withdrawals and profit shares are each independent, so a book can state all three.
A withdrawal and a profit share are each carried by their own setting at every cadence monthly, quarterly or yearly, so the same instruction never lands on a different key depending on how often it repeats. A deposit is the one flow with a single home, since dollar-cost averaging is a different instruction: it deploys the pool the book already has rather than growing it.
Cross-asset conditions#
One asset's entry can depend on another. Gate a trade on a second symbol and the splitter attaches it as a cross-asset condition on the dependent asset.
When BTC RSI crosses above 65 on 1d, if ETH RSI is above 50, buy ETHUSDT.A cross-asset condition that gates an entry runs at execution time. Cross-asset exit conditions are not live yet: a book that relies on one is refused before it runs, with the note "aux signal exit: pending parity". Rewrite the exit as a stop, a target, or a same-asset condition.
Reading the book report#
A portfolio report shows what a single backtest cannot.
-
Book equity versus its benchmark. The shared equity curve plotted against the benchmark, with a beating or trailing readout. The benchmark is an equal-weight buy and hold of the same assets unless you asked for another one; see What the book is measured against and it carries the relative numbers alongside it: outperformance, beta, correlation, alpha, tracking error, information ratio, and up/down capture.
-
Per-asset results. Either full per-asset panels (grade, equity, trades) or a Per-asset attribution table (realized P&L, trades, win rate, exposure). Per-asset return and Sharpe are drawn on an equal-base display axis, a slice of the pool, not standalone capital.
-
Skip ledger. Every entry signal that did not fill, with its reason. Read it before concluding a strategy did nothing: a book that suddenly trades less is usually a control doing its job.
Reason What happened NO_CAPITALthe signal fired but the shared pool had no cash left for it BELOW_MIN_NOTIONALthe affordable size was under the exchange minimum GUARDa per-asset gate blocked it (session window, time filter, trading guard) PRE_LISTING/STALE_BARthe asset had not listed yet, or had no bar closing on that tick PER_ASSET_CAP/MAX_CONCURRENT/EXPOSURE_CAPa size or count cap was already reached CORRELATION_LIMITits returns were too correlated with a position already open GROUP_CAP/GROUP_POSITION_CAPits sector was at its exposure cap, or already held its maximum number of names TRADE_THROTTLEthe book had already opened its allowance of new positions for the period LOSS_LIMIT_PAUSEthe book was paused for the rest of the period after hitting its loss limit REENTRY_COOLDOWNthe sleeve was still inside its post-exit cooldown LONG_EXPOSURE_CAP/SHORT_EXPOSURE_CAP/NET_EXPOSURE_CAPit would have pushed exposure past a directional cap CASH_RESERVEfunding it would have spent the book below its cash reserve floor EQUITY_CURVE_FILTERthe book's own equity was below its moving average, so it was standing down REGIME_OFFthe book-wide regime filter was risk-off on that bar DIRECTION_MAX_POSITIONSthe book was already holding its maximum number of positions on that side SLEEVE_HALTEDthat asset was retired for the rest of the run by the sleeve drawdown halt BOOK_TIME_FILTER/DATE_BLACKOUTthe book does not open on that day, hour or month, or the date fell inside a blackout window DIRECTION_BIASthe book is restricted to one side and that signal was on the other WARMUPthe book was still inside its settle period TRADE_BUDGET/TRADE_PERIOD_BUDGETthe trade allowance for the run, or for this period, was spent ENTRY_SPACINGit came too soon after the previous entry TURNOVER_CAPannualized turnover was already at its ceiling CONFIRMATIONfewer sleeves signalled together than the book requires BELOW_BOOK_MIN_NOTIONALit would have been smaller than the book's own minimum position size OPEN_RISK_CAPfunding it would have pushed total open risk past the book's budget GROUP_MAX_POSITIONSits sector/group was already holding its maximum number of positions ASSET_TRADE_THROTTLEthat sleeve had already opened its allowance of positions for the period PER_ASSET_NOTIONAL_CAPthe sleeve was already at its dollar cap for open notional PERIOD_LOSS_LIMITthe book was stopped for the rest of the period after hitting its loss limit LOSS_STREAK_PAUSEthe loss-streak breaker had paused new entries DAILY_GIVEBACK_STOPthe book had given back too much from the day's peak and stopped for the day OUTSIDE_TRADING_DAYSthe bar fell outside the weekdays the book may open on OUTSIDE_TRADING_HOURSthe bar fell outside the session window the book may open in BLACKOUT_DATEthe bar fell inside a date range the book sits out DIRECTION_NOT_ALLOWEDthe book is restricted to the other side (long-only or short-only) BELOW_MIN_VOLUMEthe bar traded less value than the book's liquidity floor BELOW_MIN_TRADE_NOTIONALthe final size, after every cap, was under the book's minimum trade size BELOW_MIN_SIGNAL_MARGINthe signal did not clear its own threshold by the required margin BENCHMARK_LAGGINGthe asset had not outperformed the benchmark over the lookback INSUFFICIENT_HISTORYthe asset did not yet have the minimum bars of history behind it ROLLING_SHARPE_FLOORthe book's rolling Sharpe was below its floor TOP_N_CONCENTRATIONthe fill would have pushed the largest N positions past their combined cap ASSET_RETIREDthe sleeve had lost too much and stopped taking new signals WIN_STREAK_BREAKERthe book was standing down after a run of winning trades ASSET_ATTEMPT_CAPthat coin had already used up its allowance of entry attempts OPEN_LOSS_GATEa position the book already held was too far underwater to take new risk DRAWDOWN_PAUSEthe book was too far below its high to open anything new, and had not recovered yet BIG_LOSS_COOLOFFthe book was cooling off after a single trade lost more than its limit BAR_ENTRY_CAPthe book had already opened its allowance of positions on that bar SIGNAL_EXPIREDa signal the book could not fund waited too long and was dropped SIGNAL_NOT_HELDthe entry condition fired but had not held for long enough SIGNAL_TOO_WEAKit cleared its own threshold by less than the other signals on that bar BETA_CAPthe book's beta to its benchmark was already at its ceiling BELOW_MIN_PRICEthe asset was trading below the minimum price the book will touch VOLATILITY_STANDDOWNthe book's own realised volatility was above its ceiling CLUSTER_CAP/SYMBOL_POSITION_CAPits correlation cluster, or that symbol, was already full LOSS_STREAK_BREAKER/LOSING_PERIOD_BREAKERthe book was standing down after a run of losing trades, or of losing days PERFORMANCE_FLOORthe book was halted because its win rate or profit factor fell through its floor ENTRY_FILTERthe book's entry filter was not satisfied on that leg's own chart PROFIT_LIMIT_PAUSEthe book had banked its profit target for the period and was done trading it Some controls act on exits or on the account rather than on an entry signal, so they never appear in this ledger. They are counted separately, under the book's risk controls: a minimum hold deferring an exit, a scheduled flatten, a sleeve being retired, positions being sized down or up by a responsive or market-state sizing rule, a position resized to a stated dollar risk or trimmed to its per-trade ceiling, an R target added to a leg, a time stop firing, an add refused by the pyramiding discipline, the book sent to cash by a regime break, a delayed fill, the book's profit floor closing it out, and each deposit or withdrawal.
Three entries in that ledger are not a control turning a signal away, and the report marks them as such:
ENTRY_FILTER_UNREADABLEmeans the filter could not be measured on that asset it did not fail the filter;NO_NEXT_BARmeans the signal fired on the last bar of the data and a next-open fill needs a bar after it; andLIMIT_NOT_FILLEDmeans the bar never traded through your limit, an execution miss rather than a rule. No fill is ever invented to hide one. -
Run warnings. What the engine decided, said out loud. Three of them mean the run's numbers are not comparable with a default run and are styled to say so:
BOOK_COSTS_APPLIED(one cost model replaced every leg's own),FILL_TIMING_NEXT_OPEN(entries filled at the next open, not the signal close) andCOMPOUNDING_OFF(every position sized off the starting capital).AVERAGED_DOWNis styled as a warning of its own, it is the only family that adds risk to a losing position. -
Money that moved outside trading. Withdrawals, profit shares and interest on idle cash all change what final equity means, so the header names them beside it. A shortfall count means a scheduled payout could not be funded in full.
-
Retired sleeves. If the sleeve-retirement rule fired, the report names which asset the book gave up on and what it cost, usually the most actionable line in the whole thing.
-
Book grade. One letter grade for the whole book, from the same grader as a single backtest, alongside book-level Monte Carlo and walk-forward robustness.
You can share a book as a read-only /sp/<token> page, and export the merged trades as CSV or the
full result as JSON.
Known limits#
These are behaviours that are correct but surprising, the ones most likely to make a run look broken when it is doing exactly what it was told.
- Spacing between entries is measured across bars, not within one. "Leave 3 bars between any two entries" stops the book opening on bar 3 and again on bar 4. It does not stop three sleeves that all signal on the same bar from all opening at once, zero bars have passed between them. To bound a single bar's burst, say "at most one new position per bar"; the two rules compose.
- A total trade budget can finish slightly over. "No more than 100 trades" stops the book opening once 100 have booked, but positions already open still run to their own exits and book as they go. The overshoot is at most the number of positions open when the budget was reached; cap concurrent positions (or new positions per bar) to bound it.
- "Require the signal to hold for N bars" cannot be met by a crossover. A crossover is true on exactly one bar by construction, so asking it to hold for two blocks every entry and the book takes no trades. That is the rule being unsatisfiable, not an absence of edge, the run says so in its warnings. Use a threshold-style entry ("RSI is below 30") for a signal that can persist.
- A stand-down phrasing names the side you sit out. "Stop trading when the equity curve drops below its 50-day average" means trade while above it. Both readings are supported, "only trade while equity is below its average" is a valid, if unusual, instruction, so the wording decides, and the report echoes the side the book actually traded on.
- A rotation that holds cash is the momentum floor working. "Only while their momentum is positive" is an instruction to sit out, so a flat stretch with no trades in a falling market is the control doing its job, not a rotation that failed to rank. The run distinguishes the two: one warning says nothing cleared the floor, a different one says nothing could be ranked at all.
- A minimum number of holdings cannot be honoured. "Always hold at least 3 positions" is declined out loud. A signal-driven book can decline exposure but never manufacture it: there is no entry to take when nothing has signalled. State it as an allocation instead ("equal weight the book, rebalance monthly"), which holds targets rather than waiting for signals.
- A liquidity floor filters bars; it does not pick names. "Require $10m of daily volume" skips entries on thin bars for the assets you named. Choosing which assets to trade by liquidity or market cap is refused: it needs a point-in-time constituent list, and ranking today's names over history would put survivorship bias into every bar.
- Spot crypto only in v1; equities are rejected. A book holds up to 20 assets.
- Mixed timeframes are allowed. The book merges assets on absolute close time and reports the equity curve on the coarsest timeframe in the roster.
- Per-asset return and Sharpe use an equal-base display axis, not standalone capital. Win rate, profit factor and trade counts are each asset's real numbers.
- If the book hits the equity floor it halts early and flags ruin.
- Futures books are not supported yet.
- Starting capital stated in the prompt ("a $250k book") is carried into the run. If you drive the
API or the MCP tools yourself, pass the
settingsobjectparse_portfolioreturns straight torun_portfolio, it holds every book-level control the prompt stated, and a book run without it is a book without your risk limits.
Next: Build a portfolio step by step, or the MCP tools to run one programmatically.