Skip to content

book ​

How do you backtest a portfolio of many names at once? Call book.target with the names and the weights of equity each should hold. The engine makes the trades on the next open, charges the costs book.config sets, and keeps one account, which book.equity() reads. A strategy() trades one symbol an order at a time, and a book trades a list by saying where it should end up.

ameno
indicator("Momentum book, long and short")
array<string> m = request.universe(20, "stablecoin")
array<float> mom = request.each(m, close / close[20] - 1)
// Stating initial_capital on a universe book also caps each fill at 1% of
// the bar's dollar volume and charges impact under it: see volume_cap.
book.config(fee_bps = 10, slippage_bps = 5, initial_capital = 100000)
// Weights of equity: above-average momentum long, below short,
// scaled so the absolute weights sum to 1.
book.target(m, cs.scale(cs.demean(mom)), reason = "20-day momentum")
plot(book.equity(), "Equity")

A book's calls do nothing inside a request.* expression, where they read na and the run warns. The Strategy Tester, the terminal's backtest report, reads a book as it reads a strategy: each name's stretch from flat to flat is a trade, and its report adds turnover, costs and what each name held. See the book.

book.config ​

AmenoScript only
ameno
book.config(fee_bps, slippage_bps, funding, fill, initial_capital, funding_venue, accounting, volume_cap, impact_bps, venue, unlisted, daily) → void

Sets the book's costs, fills and capital. Call it before the first book.target, since capital stops changing once anything has traded.

A negative cost, a fill other than "open" or "close", or a capital of 0 or less stops the script with an error.

One default depends on the rest of the call. A book that trades a request.universe list and states its initial_capital caps each fill at 1% of the bar's dollar volume, and charges impact_bps on what it takes. Any other book has no cap, and a universe book that states no capital warns that nothing limits its fills. volume_cap = 0 turns the cap off.

ParameterDefaultDescription
fee_bps
series float
0Paid on the value of every fill, in basis points.
slippage_bps
series float
0Also paid on the value of every fill, in basis points.
funding
series bool
falsetrue pays each perpetual's funding on what the book holds: a long pays a positive rate and a short receives it. A name with no funding series pays none, and the run says so. CMC: names never pay.
fill
series string
"open""open" fills each name at its first open after the close that made the target. "close" fills a name at that close when its bar closes then, and any other name waits for its next open.
initial_capital
series float
100000The money the book starts with.
funding_venue
series string
"binance"The exchange whose funding history funding reads.
accounting
series string
"book"The book's own accounting, as this page describes it. Other values are reserved.
volume_cap
series float
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. Left out, 0.01 or none, as above. 0 turns the cap off.
impact_bps
series float
100Under the cap, a fill also pays this times the square root of its share of the bar's volume: 10 bps at 1% of volume.
venueNo effect here
series string
Reserved. It changes nothing in a backtest.
unlistedNo effect here
series string
"cash"Reserved. It changes nothing in a backtest.
daily
array<string>
Names to mark and fill from their daily bars, whatever the chart's timeframe. Each trades only on the chart bar that closes with its day, at that close, so it needs fill = "close" on the book's own accounting. Set it before the first target.

See config and fills.

book.target ​

AmenoScript only
ameno
book.target(names, weights, reason) → void

States what the whole book should hold, as weights of equity, on this bar's close.

A name left out goes to zero, an na weight reads as zero, a name listed twice adds up, and a negative weight is a short. Weights needn't sum to 1. On a bar with no call, nothing trades and each weight drifts with its price. Only fills pay costs, so drift is free. A name stops trading once its last bar is 10 days old, or two chart bars when those are longer: it leaves at its last printed close and pays fee and slippage, and the run says so. A different count of names and weights stops the script.

A name the script didn't get from request.universe makes the run warn that the list was chosen today, since a list of today's survivors flatters any backtest.

ParameterDefaultDescription
names
array<string>
The symbols, an array<string>.
weights
array<float>
One weight per name, as a fraction of equity.
reason
series string
""A note the report shows beside the rebalance.

See targets.

book.equity ​

AmenoScript only
ameno
book.equity() → series float

The book's equity at this bar's close.

book.turnover ​

AmenoScript only
ameno
book.turnover() → series float

What this bar's fills traded, as a fraction of equity.

book.fee ​

AmenoScript only
ameno
book.fee() → series float

What this bar's fills paid in fees, slippage and impact, as a fraction of equity.

book.funding ​

AmenoScript only
ameno
book.funding() → series float

What the book paid in funding on this bar, as a fraction of equity. A receipt reads negative.

book.weights ​

AmenoScript only
ameno
book.weights(names) → array<float>

Each name's weight of equity at this bar's close, and 0 for a name the book doesn't hold.

ParameterDescription
names
array<string>
The symbols to read, in the order you want them back.

See also ​

AmenoScript is compatible with Pine Script v6. Pine Script is a trademark of TradingView, Inc., which has no part in AmenoScript.