Account currency
A strategy that declares currency=currency.USD on a symbol quoted in something else now has
its money converted, the way TradingView does it. Until this release the declaration was parsed
and then ignored: profits, position sizing and the margin check all stayed in the symbol's quote
currency.
The conversion happens at the cash-flow level. Every amount is converted at the rate of the bar
it is booked on, and nothing is re-marked afterwards — so a closed trade's profit is the gross at
the exit bar's rate, minus the entry fee at the entry bar's rate, minus the exit fee at the exit
bar's. Unrealized profit follows the current bar, and run-up and draw-down are marked at the rate
of the bar the extreme happened on. Percentage metrics divide two converted amounts, so they are
rate-independent, exactly as TradingView reports them.
Money-based sizing converts too: strategy.cash and strategy.percent_of_equity size a budget
that is denominated in the account currency, so the contract count comes out on TradingView's
grid rather than off by the rate.
Two things deliberately do not convert, both measured against TradingView: initial_capital is
already declared in the account currency, and so are cash_per_contract / cash_per_order
commissions, which are booked verbatim. Prices stay in the symbol's own currency — a price is a
quote, not an amount.
Supply the rate as an OHLCV file with a sibling TOML declaring its basecurrency and currency,
passed with --security; the pair is inverted automatically when only the other direction is
available. Without rate data the engine keeps running in the symbol's currency, bit-identical to
previous releases, and logs one warning per run instead of failing.
Measured on the public comparison corpus: all 82 strategies with a TradingView trade reference
now agree on net profit, with a median relative difference of 1.8e-08 — the floor imposed by
TradingView reporting its strategy figures in single precision.
New strategy builtins
strategy.margin_liquidation_price— solves the same equity-versus-margin balance the
margin-call check compares, then snaps to the instrument's price grid directionally (a long
floors, a short ceils). It is na while flat, when the position's side has a zero margin
percent, and in live broker mode, where the exchange owns collateral and maintenance-margin
tiers.strategy.convert_to_account,strategy.convert_to_symbol,strategy.account_currency—
the two conversions share one rate, so they are exact reciprocals like on TradingView. With
the defaultcurrency=NONEthe account currency is the symbol's quote currency and both
directions are the identity.
Fixes
str.formatreads its pattern the way MessageFormat does. Quotes are resolved during the
placeholder scan instead of being stripped before it, so a single quote escapes braces rather
than vanishing — a webhook body containing quoted braces now formats into real braces where it
used to raise. An unquoted closing brace outside a placeholder is literal, an unterminated
quote runs to the end of the pattern, and a placeholder index with no argument behind it prints
as itself instead of stopping the script. Type and style keywords are matched trimmed and
lowercased, while a number style keeps every character it was given. Both scans are cached,
since format patterns are constants re-formatted every bar.- The OHLCV reader snaps the base price to the declared tick grid. The v2 reader snapped
delta-decoded high/low/close but leftopen— the absolute base those deltas hang off —
untouched. A feed that answers the four prices from four separate requests can return an open
one representable step off the grid, and the snapped high then decoded below it, producing a
bar the writer's own OHLC check rejects. The base now snaps on the same rule, with the
floating-point-dust tolerance only, so genuinely off-grid data such as split-adjusted series is
left alone; files without a declared grid never snap at all. - The strategy report's money columns are labelled with the account currency, not the
symbol's, and the currency rate lookup no longer reads a rate bar that has not closed yet.