Appearance
Book
How do you backtest a portfolio of many symbols without placing an order for each one? State what the whole portfolio should hold, as weights of its equity, with book.target. The engine trades to those weights at the next open, charges for each move and keeps the account.
ameno
indicator("60/40, rebalanced monthly")
array<string> names = array.from("SPY", "TLT")
book.config(fee_bps = 5, slippage_bps = 2, initial_capital = 100000)
// On the first bar of each month, go back to 60% stocks and 40% bonds
if timeframe.change("1M")
book.target(names, array.from(0.6, 0.4), reason = "monthly rebalance")
plot(book.equity(), "Equity")Between targets nothing trades, and the weights drift with their prices. This run also warns that SPY and TLT were chosen today, which Traps explains. The book.* functions are AmenoScript only, so they are what make this script AmenoScript.
Stating a target
book.target(names, weights, reason) states the whole book on this bar's close. A name is any symbol you could chart, such as SPY, BTC-USD or a CMC: member of a universe.
- A name left out goes to zero. So does a na weight. A target is the whole book, not a change to it.
- A negative weight is a short. Weights needn't sum to 1: 0.5 holds half the equity and leaves the rest in cash, and a sum over 1 is leverage.
- A name listed twice adds up.
namesandweightsneed the same size. Otherwise the run stops.reasonis a short text kept with the rebalance this target makes, which the Strategy Tester lists.- On a bar with no call, nothing trades. Call it only when you mean to trade, as the monthly check above does, or call it every bar to hold the weights exactly and pay for each small move back.
When a target fills
A target fills at the first open after the close that made it, name by name. A name whose next bar opens later, such as a share on a coin's weekend, waits for its own next open. So with the default fill, a decision never trades on the close it was made from.
- A fill is sized on what the book was worth at the prices it fills at, before the move's cost.
- A new target before that open replaces the old one. The open goes to whichever target stood when it opened.
book.config(fill = "close")trades on the target's own close instead, for each name whose bar closes at that moment. Any other name waits for its next open.- A name on a slower or different clock, such as a daily name on a 6-hour chart, fills at its own open, sized on what the book was worth then.
book.config(daily = names)marks and fills those names from their daily bars, whatever the chart's timeframe. Such a name trades only on the chart bar that closes with its day, at that close, so it needsfill = "close"as well. Set it before the first target.
Costs and capacity
book.config sets the account, and every argument is optional. Call it at the top of the script, before the first target. It may run on every bar, as the examples' calls do, but the starting capital is fixed once the book has traded and daily once the book holds a name.
| Argument | Default | What it does |
|---|---|---|
initial_capital | 100,000 | The starting cash. |
fee_bps | 0 | Paid on the value each fill trades, in basis points. |
slippage_bps | 0 | Also paid on each fill's value. |
fill | "open" | "open" or "close", as above. |
volume_cap | None, except 1% for a book on request.universe that states initial_capital (Traps says why) | The largest share of a bar's dollar volume one fill may take. A fill past it is cut, and the rest stays as drift until the next target. 0 turns it off. |
impact_bps | 100 | Charged only while a volume cap is on: impact_bps times the square root of a fill's share of volume, so 10 bps at 1% of volume and 32 at 10%. |
funding | false | Pays each perpetual funding settlement on what each name holds. A long pays a positive rate and a short receives it. |
funding_venue | "binance" | Whose funding rates to read. |
daily | none | Names that trade at their daily close, as above. |
Only fills pay, so drift is free and going back to a weight costs a trade. A short pays no borrow fee. The volume a fill reads is one it could know: at an open, the bar before's; at a close, that bar's own. A name with no volume reading doesn't trade at all under a cap, and the run says so. A name with no funding series pays none, and the run says that too.
A name that stops trading
A name you leave out goes at its next open, like any other move. A name that has printed no new bar for 10 days, or for two of the chart's bars when those are longer, is read as gone. It leaves at its last printed close, paying fee and slippage with no volume cap, and the run and its trade both say so. A shorter gap just waits for the name's next open.
Reading the book
| Function | Returns, at this bar's close |
|---|---|
book.equity() | The account's value. |
book.turnover() | What this bar traded, as a fraction of equity. |
book.fee() | What this bar's fills paid, as a fraction of equity. |
book.funding() | What this bar's funding paid, as a fraction of equity. |
book.weights(names) | What each name holds, as a weight, and 0 for one it doesn't. |
With the default fill, a position shows on the close of the bar it filled on, the bar after the target. So book.weights on the bar that made a target still shows the old weights. With fill = "close" the fill comes at that bar's own close, and book.weights shows it there.
In the Strategy Tester
A book reads in the Strategy Tester as a strategy does:
- Each name's stretch from flat to flat is one trade.
- The Book group adds turnover, what the book paid, what the volume cap cut and what it holds.
- Ratios annualise on the chart's calendar: 365 bars a year on a crypto daily chart, about 252 on a stock's.
- Max drawdown reads closes, since the names don't make their highs and lows together.
- Rebalances lists each target that changed what the book wanted: what it held before, what it asked for, and what its fills moved and paid.
- Assets sets each name's buy and hold beside the book's own line.
A script that calls strategy() as well shows the book alone.
book.* or strategy.*
strategy.* | book.* | |
|---|---|---|
| Trades | The chart's symbol | A list of names |
| You say | Orders: enter, exit, close | The weights the whole book should hold |
| Fills | The emulator's rules: market, limit, stop, at prices after the order exists | The next open, or fill = "close" |
| Sizing | Per order | Each name's weight of equity |
| Dialect | Pine and AmenoScript | AmenoScript only |
Use strategy.* for one symbol's entries and exits with stops and limits. Use book.* when the decision is how to split money across many names.
Traps
- Names chosen today flatter the result. A book holding names you picked from today's list, rather than from
request.universe, warns that they were chosen today: the coins that died aren't in it. CMC:prices are research prices. A book on them says so, because nobody could trade at a ranking site's daily close.- Nothing limits a universe book without capital. A book on
request.universethat states noinitial_capitalwarns that no volume cap applies, so a fill can take a whole day's volume of a thin coin. book.*inside a request does nothing. It reads na, with a warning. The book belongs to the chart's run.- A target on
barstate.islastis refused. A leak-safe run stops any decision that rests on a live-only name.