Rearview Join the waitlist

Reading a run

Every run is a folder in the project's runs/, holding the script as it ran, its settings, a fingerprint of the data and every result: daily equity, fills, trades, orders and warnings. The app shows a run in eight views under the editor, View ▸ Summary to View ▸ Orders (⌘1 to ⌘8). The command line writes the same files, and rvw report turns a folder into the same HTML report.

Summary

The Summary lists the run's figures beside a run to compare with: the previous run of the same script, or one you choose in Compared with. Under the figures:

  • Growth of the starting equity, with the benchmark when the run has one. Scale switches to log, where equal heights mean equal percentage moves.
  • Exposure: long and short positions as a share of equity, day by day. Futures and forex pairs count at their full value, so exposure can pass 100%.
  • Costs: what the run paid and earned besides price moves, such as commissions, interest on cash, margin interest, borrow fees, dividends and forex carry.
  • For a portfolio of several strategies, each strategy's own figures and how their daily returns moved together.

Money is in the account's currency (Run settings, "Currency"), such as A$ for Australian dollars, and every price in its security's own.

A run that made no trades says so above its figures, and what stopped its candidates, such as "All 412 candidates were sized to zero." (see The day's choices). Show What Each Line Did shows beside the script how often each condition held (see What each line did).

Best trades says how much of the result rests on a handful of trades: the share of the closed trades' P&L the best 5 made, and the profit factor and expectancy without them, such as "The best 5 trades made 61% of the closed trades' P&L; without them the profit factor is 1.08 and expectancy $12 a trade." When the closed trades made nothing or lost, it gives what the best 5 made in money instead. It is a figure of the trades' P&L after costs, not of equity: an equity curve can't leave trades out. It needs at least 10 closed trades. With the run's data open, or an update of it that only added days, it also says how many of the 10 best trades held through days the data-quality report doubts for their security, such as a price jump or a split-like move with no split on record (see Trades). The biggest winners are the first to check: a bad print or a missing split often makes them. The HTML report gives the first line; rvw analyze gives both, the second with --store.

Every figure is defined below, with the same words the HTML report uses. A run that trades every day, because it can hold coins, annualises its Sharpe, Sortino and volatility over 365 days instead of 252 trading days. Hold the pointer over a figure in the app for the same definition.

Returns & risk

  • CAGR: Compound annual growth rate: (end equity ÷ start equity) ^ (365.25 ÷ calendar days) − 1.
  • Total return: End equity ÷ start equity − 1.
  • Max drawdown: The deepest fall of equity from its running peak.
  • MAR: CAGR ÷ |max drawdown|: return earned per unit of the worst fall.
  • Sharpe: Mean daily return ÷ its standard deviation × √252, with a risk-free rate of 0.
  • Sortino: Mean daily return ÷ downside deviation (target 0) × √252.
  • Ulcer index: Root mean square of the percent drawdown over every day: how deep and how long the falls were.
  • UPI: Ulcer performance index: CAGR in percent ÷ Ulcer index.

Activity

  • Exposure: Share of days with at least one open position.
  • Avg gross exposure: Mean of (long + |short| market value) ÷ equity, over days with equity above 0.
  • Turnover: Traded value per year as a multiple of mean equity. Buys and sells both count.
  • Closed trades: Round trips with an exit. Trade statistics count these only.
  • Open trades: Positions still open when the run ended, marked at the last close.
  • Avg bars held: Mean number of days a closed trade was held.
  • Longest drawdown: The longest span in calendar days from a peak until equity got back to it.
  • Longest recovery: The longest span in calendar days from a drawdown's low until equity regained the peak.

Trade quality

  • Win rate: Closed trades with a profit ÷ closed trades.
  • Avg win: Mean profit of the winning trades.
  • Avg loss: Mean loss of the losing trades.
  • Expectancy: Mean P&L per closed trade, after costs.
  • Profit factor: Gross profit ÷ gross loss of the closed trades.

vs Benchmark

  • Benchmark CAGR: The benchmark's compound annual growth rate over the same days, dividends reinvested.
  • Benchmark max drawdown: The benchmark's deepest fall from its running peak over the same days.
  • Beta: Slope of the strategy's daily returns regressed on the benchmark's.
  • Correlation: Correlation of the strategy's daily returns with the benchmark's, −1 to 1.
  • Alpha: Yearly return beyond what the beta to the benchmark explains: the mean daily return less beta times the benchmark's, times the days in a year.
  • Tracking error: How far the daily returns stray from the benchmark's: the standard deviation of their difference, annualised.
  • Information ratio: The mean daily return above the benchmark's over its standard deviation, annualised: return beyond the benchmark per unit of tracking error.
  • Up capture: On the days the benchmark rose, the strategy's mean daily return over the benchmark's. Above 1, it rose more.
  • Down capture: On the days the benchmark fell, the strategy's mean daily return over the benchmark's. Below 1, it fell less.

Long / Short

  • Long or short trades: Trades closed, and still open at the end.
  • Long or short win rate: Closed trades with a profit ÷ closed trades on this side.
  • Long or short P&L: Total P&L of the side's closed trades, after costs.

On intraday data (see Intraday bars in the import guide), a run steps through every bar. Trades, fills, orders and events show the bar's time, and days held are bars held. Every statistic still reads daily equity, one figure a day, so a Sharpe ratio means what it does on daily data.

Timeline

The Timeline lays the run out on one clock: equity, drawdown, how much was invested, the benchmark's trend, and a track of trades for each security. The strip at the top is the whole run; click or drag in it to move the days in view, and zoom with ⌘+ and ⌘−.

Drag across the tracks to choose an inspection range. The Inspection Range inspector (⌥⌘3) then shows the range's return, worst fall and trades beside the benchmark's. Run on This Range starts a run limited to those days. View ▸ The Day’s Choices (⌥⌘4) shows the candidates at the close of the range's last day (see The day's choices).

Holdings

Click a day on the tracks to see what the run held at its close in View ▸ Holdings (⌥⌘5); ← and → step a day either way. With no day picked, it shows the run's last day.

  • Each position: the ticker it had that day, the strategy holding it in a portfolio, the shares, coins or contracts held, the average cost of what's held and the close it's valued at (both in the security's currency), its value and its share of equity, its open P&L before costs and dividends, and the day of its first fill, with the days it has been held.
  • The account: the positions, cash, any proceeds of short sales, equity, and gross and net exposure as shares of equity.

The holdings are worked out again from the run's folder and your data's closes, the way the engine values the account, so the positions and cash come to that day's equity exactly. Futures and forex pairs settle at every close: their gains are in cash already, so they count in exposure at their full value, not in equity. The data must be the run's, or an update of it that only added days. On the command line, rvw holdings --run <run folder> --store <data store> --date 2020-02-28 prints the same as JSON.

Returns

  • Yearly returns: each calendar year from the previous year's close, beside the benchmark, with the year's deepest fall.
  • Monthly returns: a grid of each month from the previous month's close. The colour scale saturates at the largest months, and the help tag says where.
  • Rolling 12 months: the return and Sharpe ratio over the 252 trading days before each day, or the 365 days of a run that trades every day. A run shorter than that shows none yet.
  • Worst falls: the five deepest falls from a peak, each with its peak, its bottom, the day equity got back to the peak (or "not yet"), its depth, and the calendar days to the bottom, to recover and in all. They're measured as the max drawdown is, on returns that leave out deposits and withdrawals, so the first is the run's max drawdown. Double-click one to see it on the Timeline as the inspection range.
  • By condition: does the strategy earn only when the market rises, or only when it's calm? Split by Condition… takes a condition written as in a script, such as close("SPY") > sma(close("SPY"), 200) or close("VIX") > 25, and splits the run's days three ways: those after a close it held at, those after a close it didn't, and those after a close it was unknown at (a missing value, or before its lookback, such as the first 200 days of a 200-day average). Each day goes with the condition at the close before it, so a day's return is never split by what was known only at its end. For each, the table gives the days, which add up to the run's; growth per year from those days' returns, beside the benchmark's on the same days; the Sharpe ratio; the deepest fall of those days' returns chained together, leaving out the days between; and the average exposure. It also gives the trades entered at a close the condition was in that state at, their win rate and their P&L, open trades at their last close. Growth and the Sharpe ratio are annualised over the run's own year, 365 days for a run that trades every day, and left out for a state of fewer than 20 days, where they would mislead. The condition is of the market: it reads other tickers by name, or your series, never the bars of the securities the strategy trades, so close > sma(close, 200) is refused under the field. A series of each security's own numbers is refused too, the first day it holds for one security and not another; a series with the same value for every security that has it works. It's worked out on the run's data as the run saw it, behind its lockbox, so the run's data, or an update of it that only added days, must be open, and like a run it needs the licence or the trial. The window keeps the condition while it's open, and splits each run you show by it. rvw analyze --run <folder> --store <store> --when "<condition>" prints the same as JSON.

Trades

The trade list has one row per trade: the security under the ticker it had then, the side, entry and exit, P&L, the return over the entry value, the quantity, bars held and why it ended. Control-click a column title to add the entry and exit prices, MAE, MFE and flags such as a delisting or a forced liquidation; the selected trade shows its flags beside its chart's title either way. A futures trade's value counts its point value, its P&L includes every day's settlement, and one held through a roll is flagged *Rolled*: its entry price is the first contract's and its exit price the last one's. Prices are in each security's currency and P&L in the account's, so a trade priced in another currency than the account's has no return; a forex trade's P&L includes its carry, and one that counted a missing interest rate as zero is flagged *Assumed rate*. Show narrows it to one side, winners or losers; a portfolio adds a strategy menu.

  • Click a trade to see its chart with the entry and exit marked. ↑ and ↓ move through the list.
  • A trade still open when the run ended is marked at the last close and labelled open. Trade statistics count closed trades only.
  • A trade held through a day the data-quality report doubts for its security, from its entry through its exit (or the run's last day while open), has a warning flag beside its ticker. Its help tag names each finding's code and date, such as SUSPECT_SPLIT for a possible missing split, and clicking it opens the Chart on the first of those days. The flags need the run's data open, or an update of it that only added days.
  • Distribution shows how the closed trades' results spread, how long they were held, and the share that ended each way: a signal, a stop, a target, a time limit.
  • By Security sums the trades by security (its name in the ticker's help tag) with its sector: trades, win rate, P&L (open trades at their last close), that P&L as a share of the starting equity, commissions, dividends, days held, and the first entry and last exit. Group sums them by sector or industry instead, as your data's securities file names them now, not as they were then; a security without one is *Unclassified*. A portfolio's strategy menu shows one strategy's. Double-click a security to open it on the Chart. The line under the table accounts for the rest of the equity: from the starting equity to the ending, what the trades made, any deposits and withdrawals, and what no trade made, which is interest on cash, margin interest and short rebates.
  • MAE/MFE plots each trade's maximum adverse excursion (the worst it stood while open) against its maximum favourable excursion (the best). Trades that went far in your favour and gave it back show where a target or trailing stop might belong, and how far winners fell first shows how tight a stop can be before it cuts them.

Chart

The Chart (⌘5) shows one security on the run's days: its prices as traded, the run's buys and sells on them, and what the script computed each day.

  • The security is the one the run traded most, or, for a run without trades, the data's first stock or ETF. Choose another from the menu at the top: the run's traded securities, or any security in the data by ticker or name, including ones the strategy never entered. A portfolio adds a menu of its strategies.
  • Lines. The script's numbers are drawn day by day, in that day's prices. An average or a band lies over the candles; another number, such as an RSI or an ATR, goes in the pane below, one at a time. The chips above the chart turn the price lines on and off and choose the one below.
  • Lanes under the chart show, each day, whether the security was in the universe, each condition of the script (filter, enter long, exit and the rest) and each true-or-false binding: filled where it held, shaded where it was unknown, as during an indicator's warm-up. Entry possible marks the days an entry could be placed: the conditions held and the rank and size were known. Rank, free slots and the rebalance schedule decided which of those became trades; hover over one to see how it fared, such as "14th of 31, 3 free slots: no free slot".
  • Hover for the day's prices, every line's value, every condition and any fill. The strip at the top is the whole run: zoom with ⌘+ and ⌘−, and click or drag in the strip to move along it. Zoomed far out, the candles become a line of closes.
  • Show on Chart, on a trade's chart or in the trade list's menu, opens the Chart on that security with the trade in view. In the editor, a binding's figure has Show on Chart too.

Rolls. On a futures chain a dashed line marks each roll, labelled with the contract rolled into; hover for the prices of both contracts. The candles are as traded, so the price steps at a roll while the script's lines, read across rolls by ratio, don't.

The lines are the run's: its script, params and data. A run still charts on data updated since with days added. A run made from a signals folder, a walk-forward's joined run or a search has no single script, so its chart shows prices and fills, and says why there are no lines. On the command line, rvw trace writes the same day-by-day record (see Writing scripts with an AI agent).

The day's choices

At each close, a strategy ranks its entry candidates and fills its free slots in rank order. The day's choices say what became of every candidate, so "why didn't it buy X?" has an answer.

  • Dropped before ranking: outside the strategy's markets; signalled long and short at once; a short signal on a coin; held already (by this strategy or, in a shared pool or portfolio, another); being sold; entered by another strategy at the same close; no bar to trade; no exchange rate yet for its currency.
  • Ranked: the rest, by score, ties going to the lower security id, each with its place and the free slots at that close.
  • Then, in rank order: ordered; sized to zero or with no ATR to size by, which use no slot; no limit or stop price; or no free slot when its turn came. A rebalancing strategy's holdings are kept as they were, bought up or sold down to their target, or sold for a better-ranked one.
  • At the fill: an ordered candidate shows its order's status (filled, partly filled, cancelled, rejected, or not filled yet at the run's end), so a share count that shrank after a gap shows.

The whole run adds every close up, a security counted once at each close it was a candidate at: the candidates; those dropped before ranking, and why; those ranked; those ordered, and how their orders ended; and those not ordered, and why. Its headline names what stopped the most candidates. A strategy whose positions are too small to buy one share or contract, such as a futures strategy on $100,000, says "All 412 candidates were sized to zero." The Day’s Choices inspector shows the whole run under the day's choices, the Summary of a run that made no trades shows its headline, and rvw choices --summary prints it as JSON.

The choices are made again from the run's folder, on its data or an update that only added days, behind its lockbox: the run is simulated once more, so they take as long as the run did. They change nothing, and the orders they make must be the run's, or Rearview says the run doesn't reproduce on this data. The Orders tab lists the candidates at the last close, The Day’s Choices inspector a close you choose on the Timeline, and the Chart's readout one security's. A run made from a signals folder doesn't keep its signals, so the app can't list its candidates; rvw choices can, given the signals and config again.

Assumptions

  • Simulation: the rules this run followed, in words: the fills, the account and margin, costs, delisting haircuts, and anything a portfolio or a rebalance added. A run that can hold futures says how they settle, roll and are backed or margined. One in another currency than US dollars, or holding securities in other currencies, says how it converts; one that can hold forex pairs, how they settle and earn carry; one on other exchanges' days, which ones and how a year is counted.
  • Warnings: what the engine noticed about this result, each with its code, such as a delisting whose reason the data didn't give (DELIST_ASSUMED) or a ticker that starts after the run's first day (SYMBOL_STARTS_LATE).
  • The run's identity: the engine, protocol and language versions, the data fingerprint and whether it matches the project's data now, and the script exactly as it ran. Reveal in Finder shows the run's folder.

Robustness

Every try sets the run against every try of its script in the project: its runs, previews, search trials and walk-forwards on the same data, the run among them. It gives the run's Deflated Sharpe after allowing for that many tries, with every input, and the answer also shows on the Summary as Tried N ways. See Every try counted.

Random entries sets the run against 500 runs whose entries were drawn at random at the strategy's own rate, keeping its exits, sizing and costs, and says whether its entries did better than chance. It runs when you choose Compare with Random Entries, and the answer also shows on the Summary. See Random entries.

Stress tests run the strategy again under the frictions of trading it: costs doubled, every signal acted on a day late, entries missed at random, and started on each of its next 20 days. Each sits beside the run's own figures, with no verdict. They run when you choose Stress Test. See Stress tests.

Capacity runs the strategy again at 1, 3, 10, 30 and 100 times its starting cash, every dollar amount scaled alike, each under a volume cap: the run's own, or 1% of each bar's volume. Each scale has the run's figures, the share of what it meant to buy that the cap cut, and its orders' share of their bars' volume, with no verdict. It runs when you choose Run Capacity Test. See Capacity.

Monte Carlo replays the run's daily returns in other orders, many times, in blocks of consecutive days whose lengths are random, so streaks and quiet spells stay together. It shows the spread of growth per year and of the deepest fall across the replays, beside the run's own.

The replays assume the past's returns could have come in another order. They can't show a risk this history never had. They need at least 20 daily returns, and the same seed always gives the same replays.

Trade order replays the closed trades one after another in other orders: the spread of the deepest fall and of the longest losing streak, and both as traded. See Trade order.

Orders

After the last day of the data, Orders lists what the strategy would do at the next session, ready to export. See Next-day orders. Under them, Candidates at the last close lists every security the strategy could have entered and what became of it.

Scans

A scan lists the securities that meet a script's conditions on a day, with every value it computed, best ranked first. ⌘R on a scan script shows its rows here in place of a run, and Run ▸ Scan Candidates shows a strategy's entry candidates. See Scans.

The Runs library

The Runs navigator (⌃⌘2) keeps every run, newest first.

  • All Runs, Today, Flagged, each script and each tag are folders in the sidebar. Flag runs, tag them, and group them by script, tag or day.
  • The search field matches words in a run's script name and params, and also takes figures and marks: sharpe > 1, cagr >= 5%, dd < 20%, trades > 100, tag:candidate, script:rsi2_pullback or flagged.
  • Select two or more runs (⌘-click) to compare them: their growth of $1 and every figure side by side, with the best value of each row marked. If the runs differ in more than the strategy (other data, another engine version, other settings), the comparison says so first, because their figures can't be compared one to one.
  • With exactly two runs selected, What Changed goes from the older run, the first, to the newer, the second:

    • A sentence on how much more or less the second run made on closed trades, and where that came from: trades only it took, trades only the first took, and shared trades it exited earlier or later, sized differently, or closed for another P&L. Its amounts add up to the difference.
    • The script's lines added and removed, with the unchanged ones folded away; a portfolio's strategy by strategy.
    • Each setting and param that differs, with both values, as config.json names the setting.
    • The trades, matched by strategy, security, side and entry day (a security entered twice on one day pairs in the order of entry): those in both runs whose exit, quantity or P&L changed, those only in the first, and those only in the second, each group's P&L summed. Open trades are listed but left out of the sums. Double-click a trade to see it on the Chart.

    Runs on different data are compared all the same, and the sentence says so. On the command line, rvw compare <first run> <second run> prints the same as JSON.

  • Delete moves a run's folder to the Trash.

Searches and walk-forwards are listed in the project navigator, under Searches and Walk-Forwards.

Exporting

  • File ▸ Export ▸ Report (HTML)… writes a run, search or walk-forward as one HTML page, with its assumptions and warnings. To make a PDF, open it in a browser and print it.
  • File ▸ Export ▸ Trades and Equity (CSV)… writes the trade list and the daily equity as CSV files.
  • File ▸ Export ▸ For Python… writes the same two files with the daily returns, every fill and the daily positions, for pandas, quantstats and pyfolio.

Every export carries the notice "Hypothetical results, based on hindsight, not actual trading. Orders come from your script and your data. Verify before placing." It is the first line of each CSV, after a #, and the foot of the HTML report. A licensed copy adds the licensee's name to the HTML report.

For Python

  • returns.csv: each day's return after the first, as a fraction. strategy is measured as the run's figures are, so deposits and withdrawals aren't gains; benchmark is there when the run has one.
  • transactions.csv: every fill, with the ticker it had that day, the amount (shares, coins or contracts, below 0 for a sale or a short) and the price. A split changes the shares held without a fill, and a security that changed its ticker is listed under each in turn.
  • positions.csv: each day's positions at the close, by ticker, at their value in the account's currency (below 0 for a short), then cash, which includes any proceeds of short sales. They add up to the day's equity. Futures and forex pairs settle at every close, so their gains are in cash already: their value is their exposure, which isn't part of equity.

Positions are worked out again from the run's folder and your data, as View ▸ Holdings does, so they need the data the run was made on, or an update of it that only added days. Without it, the export leaves positions.csv out, removes one an earlier export left in the folder, and says why. On the command line, rvw export --run <run folder> --out <folder> --python --store <data store> writes the same.

Each file starts with the footer line, so read it with comment="#":

import pandas as pd
import quantstats

returns = pd.read_csv("returns.csv", comment="#", index_col="date", parse_dates=True)
transactions = pd.read_csv("transactions.csv", comment="#", index_col="date", parse_dates=True)
positions = (pd.read_csv("positions.csv", comment="#", parse_dates=["date"])
             .pivot(index="date", columns="symbol", values="value").fillna(0))

quantstats.reports.html(returns["strategy"], returns["benchmark"], output="tearsheet.html")

positions is then in pyfolio's wide form, one column per ticker and cash.

Checking a run

  • Check It Reproduces…, in the context menu of a run, search or walk-forward, runs it again from its own folder, on the data and settings it was made with, and compares every file. Nothing in the project changes. If the data or the lockbox has changed since, it says so and runs nothing.
  • Check Runs Reproduce…, in the Runs library's context menu, checks every run of the project this way, one after another, and lists each one: identical; provenance only, the same results from another engine version; different, with the first file and line that changed; or can't check, with the reason, such as a run made on other data. Under how many came out each way, those that differ come first, and Show in Runs beside one selects it in the Runs library. Stop it at any time.
  • When some runs were made by another version of Rearview's engine, after an update, the Runs library offers this check, and What Changed opens the changes to results under Golden replays.
  • In Terminal, rvw repro --all <project> --store <data> checks a project's runs the same way, and --searches adds its searches and walk-forwards, which take longer.
  • Help ▸ Report a Wrong Result… bundles a run for support: the script, settings, engine version and data fingerprint, your note and, if you choose, the trade list. It holds none of your data's bars. Support replays it with rvw repro against the same engine.