TradeOnReplay guide
Everything you need to use TradeOnReplay. Pick a section below or search (Ctrl K).
Sections are added as each part of the app is documented.
Everything you need to use TradeOnReplay. Pick a section below or search (Ctrl K).
Sections are added as each part of the app is documented.
The first thing you see: where you left off, how your practice is going, and a way to start the next session. Start with a topic below or search (Ctrl K).
An icon rail down the left edge, the working pages beside it that run edge to edge (with Themes, Licence and Help at the bottom), and nothing that leaves your computer.
| Page | What it is for |
|---|---|
| Dashboard | Where you left off, how much you have practised, how it is going. |
| Sessions | Every saved session in a list; pick one to see its numbers and notes beside it. |
| Trades | Every closed trade from every session in one filterable table. |
| Analytics | Charts and figures across all your trades. |
| Prop firm | Challenge attempts, their rule reports, and your odds of passing. |
| Data | Every dataset you imported: its dates, size, clock, which sessions use it, and deleting it. |
| Themes | Every colour in the app: built-in themes, your own, and a legibility check. |
| Licence | Your trial or licence, and the key. |
| Help | Feedback, diagnostics, backup and restore, privacy and the open-source notices. |
Each page is a header with its title and main action, a thin bar of controls (a filter, a range, tabs), then two panes that scroll on their own: a list or filters on the left, the detail or figures on the right. Nothing floats in a card in the middle of the screen. Figures meet at ruled lines. Pages that are mostly reading, such as Licence and Help, sit in one centred column instead.
The + at the top of the rail opens a small menu: Backtesting session, Prop firm challenge, or Import data. The first two open the new session form. If you have not imported any market data yet, they take you to the import wizard instead.
Press Resume on a session to open it in the replay window. Exit session in the title bar saves it and brings you back here, to the page you left.
The page that opens first. The pane on the left holds your most recently used session, ready to resume; the right-hand side is how it is going.
The top of the left pane shows the session you last worked on, with its profit and loss, win rate, trade count and a small balance curve. Resume opens it; Analytics opens the analytics page narrowed to that one session. At the bottom of the pane are three shortcuts: a backtesting session, a prop firm challenge, and importing data.
With no sessions yet the page shows three short steps instead: bring in price data, start a session, trade and review.
The two controls in the bar under the header narrow every figure on the right. All / Backtesting / Prop firm picks the kind of session. The range picks Lifetime, or only sessions you worked on in the last 30 or 7 days.
| Figure | What it counts |
|---|---|
| Time invested | Real time with a session open and the window focused. A minimised or unfocused window does not count. |
| History replayed | Market time you have moved through, from where each session began to where it is now. |
| Trades taken | Closed trades, with the share that were buys and sells. |
| Overall win rate | Winning trades divided by all closed trades, with the net profit and loss. |
If any trades have not been reviewed, an amber banner says how many and takes you to the Trades page. Below are four charts: hours invested by month, win rate by month, trades by market session (with each session's hours on your data's clock) and trades by symbol. Hours are grouped by the month a session was last worked on, which is an approximation when a session spans several months.
Under the latest session, the eight most recent as rows, each with its profit and loss and a 3 px mark on the left edge (green or red). Click one to open it on the Sessions page.
Every saved session in a list on the left, newest first; the one you pick fills the right-hand side.
Search by name, symbol or strategy; filter to backtesting or prop firm; sort by recently used, oldest, best P&L, most trades or name.
Delete asks first, and removes the session, its trades, its journal and its screenshots. It cannot be undone.
Below the actions you get the strategy and description (edit them and press Save), net profit and loss, profit factor, expectancy and time invested, the equity curve, monthly and daily performance, and the latest trades five at a time. The pencil beside a trade opens that session with the trade's journal entry showing.
All your closed trades in one table.
Use the same filters as analytics (type, asset, side, outcome, tags, grade, session, strategy, market session, day, time, timezone and date), the search box, and the To review button, which shows only trades you have not marked as reviewed. Click a column heading to sort by it.
Each row shows when the trade closed, its session, symbol, side and lots, profit and loss, R, how long it was held, its grade, its first two tags, and a tick if it is reviewed. Click a row (or press Enter on it) to open that session with the trade's journal entry open. Export CSV saves exactly the rows you are looking at.
A price file has times but no time zone, so the app has to be told which clock they are on.
It matters because anything defined in real time depends on it: which market session a trade belongs to (London, New York, Asia), and what hour and weekday it falls on. If your file is in Kenyan time (UTC+3), then 15:30 in the file is 12:30 UTC, which is New York. Read as if it were UTC, the same trade would look like London's afternoon.
Changing the clock never touches the candles: the digits in the file are the same either way, only their meaning is corrected. It is saved with the dataset, so every session, chart and analytics view on that data follows at once. Nothing needs re-importing.
| Where | Effect |
|---|---|
| Market session charts and filter | Sessions are real-time windows (Asia 00–07, London 07–12, New York 12–21 UTC), found by converting your data's clock to UTC. |
| Hour, weekday, calendar | Shown on your data's own clock by default. Pick another zone in the filter's Timezone to read the same trades on it. |
| The chart itself | Unchanged: the replay always shows the file's own times. |
The session windows are fixed in UTC, but a zone that observes daylight saving shifts against them for part of the year. For broker server time the app converts each trade with the offset in force on that date.
Everything that opens over the chart follows one of two shapes, so it is always clear what you are looking at.
Indicators (I), Objects (D), Chart layout (L) and a chart's Properties (right-click the chart, Properties) are panels down the right edge of the window. They start under the toolbar, so the playback controls stay reachable beside them, they have no dimming backdrop, and the chart stays live: change a colour and watch the candles change. Esc, the × or a click anywhere outside closes one, as a popup would.
The chart type button on the toolbar offers Candles, Hollow candles (a rising body left empty), Bars (OHLC), Line, Area, Heikin Ashi, Renko and Range bars. Orders and fills always use the real prices, whatever the chart shows.
The colour swatches open a palette of ten shades for each colour, with a hex field for any other.
Jump to date (G), Save template and the Keyboard list (?) are small centred dialogs with an action bar. Esc or a click outside closes them.
The floating Buy and Sell box keeps its shape: price on each button, a lot stepper between them, and a chevron (Z) to expand it into the full ticket with Market, Pending and OCO. It is drawn with the same square structure and 6 px controls as the rest, and can still be dragged by its grip.
A Long Position or Short Position box (Forecasting tools) can become a real order. Select it and press Enter, or press Place order on its toolbar:
Left as a drawing, a box plays its own trade on the chart: a candle any part of the box covers counts, including one its left or right edge cuts through.
The chart's right-click menu shows the shortcut beside Indicator List and Object List, and ticks the toggles that are on. Add price alert at sets an alert at the price you clicked: playback stops when a bar reaches it. Refresh chart redraws that chart from scratch without touching your session, drawings or settings; use it if a chart ever goes blank. The app also does this by itself when the chart library reports a drawing error. An indicator's gear (in the legend, top left of the chart) opens a compact settings card with the same rows as every other panel.
Every colour in the app, and the chart's, defined in one place. The Themes page is at the bottom of the rail.
Click a theme in the list. The whole app changes at once, the chart included, and the choice is remembered. There are five built in: Ink (true black with amber, the default), Graphite (dark), Daylight (light), Midnight (blue-tinted dark) and Paper (warm light with dark ink outlines and a green accent). In a session, the theme list is also in Chart layout.
A panel on the page checks the pairs that matter against recognised contrast ratios: primary text 7:1, other text 4.5:1, and accent, profit, loss and caution colours 3:1, all against the ground, plus the outline colour. A failing row is marked in amber with its ratio, so you can see a problem before you live with it. It advises; it never blocks.
Export writes a theme to a .theme.json file; Import reads one. An imported file is checked: a colour that is missing or not a hex value is replaced by the matching colour from the light or dark base, so a damaged file cannot leave anything invisible. The built-in themes are fixed; delete only removes your own.
Choosing a theme gives every chart that theme's candle, wick, background and grid colours. The built-in themes draw candles in black and white: on a dark background a rising candle is white and a falling one is dark with a white outline, and the reverse on a light background. A single chart can still override them from its own Properties, and Reset to defaults in Chart layout brings back the theme's.
These colours are not part of a theme. The Buy button is bright blue and the Sell button bright red in every theme, so the two can always be told apart, including in a theme whose accent is amber.
A chart floated into its own window and the pop-out analytics window follow the theme too.
Open Indicators (I) and search by name, ticker or family (rsi, bill williams, ma). Each one's gear sets its periods, source and colour.
They follow MT5's definitions, so the numbers match what you know. A few are worth knowing:
How much traded at each price, as bars across the chart. Four kinds, all built only from bars the replay has shown.
Forex and CFD data carries tick volume (how many price changes), not traded contracts. It follows real activity closely enough for the shape of a profile, so read the shape, not the totals. Where your data has one-minute bars (any import does), a profile is built from the minutes inside each candle, so volume lands on the prices each minute actually traded.
The levels and zones price-action traders mark by hand, drawn for you as the replay goes. Open Indicators (I) and search for the tool, or type ict to see them all.
Each one only reads candles the replay has already shown. A swing counts once the bars after it have closed, and a gap only fills when a bar has traded through it, so what you see on a bar is what you would have seen live. Each tool's gear sets what it shows.
Sessions and kill zones are read from the time zone your data is in. If the boxes sit an hour or more off, check its clock under Data and the clock.
See at a glance which market was open when each candle formed. Right-click the chart and tick Trading sessions.
Each session is shaded in its own colour behind the candles, with its name at the top when there is room. Where two are open at once, such as London and New York in the afternoon, the overlap gets a colour of its own, a little stronger, so the busiest hours stand out.
| Session | Hours, in the city's own time | Colour |
|---|---|---|
| Sydney | 07:00 to 16:00 | Violet |
| Tokyo | 09:00 to 18:00 | Blue |
| London | 08:00 to 17:00 | Amber |
| New York | 08:00 to 17:00 | Green |
Tokyo, London and New York are shown at first. Pick which ones in Chart layout, under Trading sessions, which also lists today's hours for each on your data's clock.
Hours are kept in each city's own time, so they move with its daylight saving and not with yours. London and New York change on different dates in spring and autumn, so for a week or two each year their overlap is an hour longer or shorter than usual; the shading shows that. Sydney's summer is the northern winter, so it moves the other way. Weekends are never shaded.
The shading reads your data's clock (see Data and the clock), so set it correctly or the bands will sit in the wrong place. The analytics and Play only some sessions still use the fixed Asia, London and New York windows in UTC.
The Data page in the rail lists every dataset you have imported.
Each row shows the symbol and name, the dates it covers, the number of one-minute bars, any gaps found when it was imported, its size on disk, and the sessions that use it. The Clock drop-down sets which time zone its times are in, the same setting as in Data and the clock.
Delete asks first, then removes the dataset and its prepared timeframes from this computer. A dataset that a session uses, as its main instrument or one of its others, cannot be deleted: delete those sessions first, or keep it. To get the data back later, import the file again.
The Help page, at the bottom of the rail, shows the app's version and gathers what you need when something goes wrong or you change computers.
The app asks whether a new version exists a few seconds after it starts. When one does, a band on the home screen says so: Install and restart downloads it, checks its signature, installs it and opens the new version; Later hides the band until the next start. It never interrupts a session. Check for updates on this page asks at any time. Updating keeps your sessions, data and licence.
Back up everything asks for a folder and copies into it, as one new folder, your sessions, journal and screenshots, datasets, scripts, templates and settings. Restore from a backup reads such a folder back, on this computer or another. Restoring adds what the backup holds and replaces files with the same name; nothing that exists only on this computer is deleted.
The licence and the trial are not in a backup, since they belong to one computer. On a new computer, deactivate on the old one and enter your key again; see your licence.
Everything stays on this computer. The app goes online only to check your licence key (the key and a device code, nothing else) and to ask whether a new version exists. No trades, prices or usage are ever sent.
The page lists the open-source software the app is built on, with each licence. Charts are drawn with TradingView Lightweight Charts™.
Practise the hours you actually trade. Pick the market sessions to play, and the replay skips everything else, quickly.
A skip is not a jump over unseen history. Every skipped bar still runs through the engine, so stops, targets, pending orders and a challenge's limits act on it exactly as if you had watched it. Only the drawing and the waiting are skipped, and playback stops by itself at the end of the data.
| Choice | What happens |
|---|---|
| Let it run | Playback carries on bar by bar, session or not, until the trade ends, then skips ahead. A trade opened by a resting order during a skip also stops the skip, so you see it. |
| Close it at the session end | The trade closes at the session's last bar, marked Session end in the trade list, and any resting orders are cancelled, since they belonged to the session that ended. |
Sessions are fixed real-time windows (Asia 00–07, London 07–12, New York 12–21 UTC), the same as in the analytics. On a daily chart every bar covers every session, so nothing is skipped. See Data and the clock for setting the data's time zone.
Choose Backtesting or Prop firm challenge at the top, fill in the basics, and start.
When the file you import has a spread column, every bar keeps its own spread and fills use it, minute by minute: a short's stop is tested against the ask that minute really had. The import preview shows a Spread column and says so. That real spread wins over variable spread; the spread you type in the import is then only for a bar that has none.
<SPREAD> column. It is read as it is.Both are saved with the session, so resuming one keeps its costs. A trade's entry and exit prices include them, and so does every figure built from them.
The basics are on the left. For a challenge, the whole rulebook is on the right, with the profit target, maximum loss and daily loss limit typed as a percentage of the account. See Starting a challenge.
Replay this trade in the journal (or Replay in the History table) opens this form pre-filled to start at that trade's entry bar with the future hidden again. Start a copy on a session does the same with that session's balance and strategy.
In a replayed trade, a bar under the toolbar holds the original trade (its side, entry time, lots, stop and target) and, as you trade it again, your retake's result beside it with the difference from the original's. It only compares: it places nothing for you, so the bars ahead stay unseen.
The app replays from one-minute bars. Import a file once and it is kept on this computer; every session you start from it reads the same data.
A new install opens with DEMO/USD, a practice market the app makes for you on the first start: two years of one-minute bars that move like a real currency pair, with quiet and busy hours and closed weekends. Press Try it now on DEMO/USD on the dashboard to start a session on it at once. Its prices are made up, so use it to learn the app, then bring in real data to backtest. It is on UTC, so the session shading lines up. You can delete it on the Data page.
The app does not download market data for you. Free sources such as Dukascopy allow personal downloads under their own terms, so you fetch the file in your browser and the app reads it.
Import a newer file for an instrument you already have and, on the confirm step, set Save as to Add to that dataset. Bars at minutes it does not have yet are added and every timeframe is rebuilt; minutes it already has are kept as they are, and the result says how many of each. The file must be on the same clock as the dataset; the wizard tells you if it is not.
| The file has | Example |
|---|---|
| Year first, with dots, dashes or slashes | 2024.01.31, 2024-01-31, 2024/01/31 |
| No separators | 20240131 |
| Day first | 31.01.2024 (Dukascopy), 31/01/2024, 31-01-2024 |
| Month first (US) | 01/31/2024 |
| A count since 1970 | 1704205800 in seconds, or in milliseconds, microseconds or nanoseconds (Binance, TradingView). The unit is told from the size of the number. |
| A time with a zone | 14:30:00Z, 14:30:00+03:00, 14:30 UTC |
Column names are matched loosely (time, Gmt time, open_time, timestamp and date with time all work). With no header row the order is date, time, open, high, low, close, volume.
A date that does not exist (a 13th month, 30 February) is refused, never turned into a plausible-looking timestamp. Rows that cannot be read are skipped and counted in the report.
A file of ticks, one row per price change with a bid and an ask, is read too. The app builds one-minute bars from the bid as it reads, and each minute keeps its real spread, the average of ask less bid over that minute, and its number of ticks as its volume. The import step says when a file is tick data and shows the first minutes built from it. Fills then use each minute's own spread, so a rollover spike in the data is a spike in your fills.
Gmt time, Ask, Bid), files with bid and ask columns from other downloaders, and TrueFX files, which have no header.The file is read as it streams, so a large one does not fill memory; it takes longer than a file of bars, about the time it takes to read the file once. Bars a few seconds apart (second bars) are built into minutes the same way.
The ticks themselves are kept with the dataset, about 16 bytes each, and the Data page shows how many. In a session, the ticks of the few days around where you are are kept in hand, and orders are filled by walking them in order:
On a dataset made from ticks, the main chart's timeframe picker also offers S5, S15 and S30: bars of 5, 15 and 30 seconds, built from the ticks. They are offered while the replay is inside the stretch the dataset has ticks for, and the time axis shows seconds. Orders on them fill from the ticks of each bar, never from the minute the bar sits in, which would hold seconds still to come. The session's other instruments keep one-minute bars.
On any dataset, with or without ticks, a pending order now fills at the minute the price reaches it, and the position it opens is only tested for its stop and target from then on. Before, on an hourly chart, a move earlier in the same hour could stop out a position that did not yet exist.
The import step shows the format it detected and lets you change it. Change it and the file is read again with your choice. This matters for dates like 03/04/2024, which could be 3 April or 4 March: when the data cannot settle it (no day above 12 in the first rows) the importer reads it day first and says so, and the first rows shown beneath tell you at a glance whether that was right.
Most files carry times with no time zone, so you say which clock they are on. This decides which market session a trade falls in and what hour and day it counts for. MetaTrader exports are usually in the broker's server time (often GMT+2 or +3), not your own: compare the first rows with a chart if unsure.
A file that states its own zone needs no answer. An epoch count is always UTC, and a trailing Z or +03:00 is applied as the row is read, so the clock is set to UTC for you. You can change the clock later without importing again; see Data and the clock.
Pick the market from the list (forex, metals, index CFDs, futures, stocks, crypto) and the contract size, smallest trade, lot step, spread and commission fill in. Any of them can still be changed. The one that matters most is the contract size: it is the account-currency value of a 1.00 move in price, per lot, and it multiplies every profit and loss figure. Typical values are 100,000 for forex, 100 for gold, 50 for an E-mini S&P 500 future, and 1 for a share or a coin. Where brokers differ (index CFDs especially) the picker says so; check your broker's contract specification.
For futures a lot is one contract, so only whole numbers make sense. For shares it is one share. Use split- and dividend-adjusted data for stocks, or the history will show jumps that never traded.
The controls at the right end of the toolbar move the replay one bar at a time, or play it on its own.
Stepping back moves only what you see. Your trades, orders and balance are not rewound, so a trade you closed stays closed. A challenge that forbids rewinding does not allow it at all.
The slider sets how fast it plays, in five steps. 1× and 2× are slow enough to follow every candle; from 3× on it is for getting somewhere (a level, an alert, the next session), and 5× is the fast mode.
| Speed | Key | Candles per second |
|---|---|---|
| 1× | 1 | 1 |
| 2× | 2 | 2 |
| 3× | 3 | 5 |
| 4× | 4 | 12 |
| 5× | 5 | 24 |
Click a stop on the slider, drag the handle, or use the arrows either side of it. With the slider focused, the arrow keys change speed instead of stepping. Hover it for the meaning in words.
The screen is redrawn at most 12 times a second. Past that, each redraw brings two or more new candles at once, so 24 a second holds even on a slow computer, with several symbols open. A price alert still stops on the very candle that touches it. A very heavy script can still slow a redraw down; hiding indicators, or using a lighter script, helps.
With a session plan active, the bar also shows a skip button (N) that leaves the current market session now. See Play only some sessions.
The order ticket is the box floating over the chart. Buy and Sell with the lot size in the middle is the short form; the chevron (Z) opens the full ticket.
One R is the distance from your entry to your stop, in money. A trade that makes twice what it risked is +2R. Results in R let you compare trades of different sizes, which is why analytics shows them. A trade with no stop has no R.
The replay resolves fills on one-minute bars. When both a stop and a target sit inside one bar there is no way to know which came first, so the stop is assumed to fill first. This is the pessimistic choice and the trade's journal entry says when it happened.
The first 14 days include everything. After that you keep a free version, or you unlock the full one with a single payment.
It starts the first time the app opens. Every feature is on. A thin banner appears in the last week and says how many days are left. The clock only counts forward: setting the computer's date back does not add days.
| Free | Full version | |
|---|---|---|
| Replay, orders, fills and drawings | All | All |
| Your journal, importing and exporting your own data | All | All |
| Saved sessions | 3 | Unlimited |
| Indicators on a chart | 2 | Unlimited |
| Custom scripts | No | Yes |
| Prop firm challenges | No | Yes |
| More instruments in a session | No | Yes |
| More than one chart pane or window | No | Yes |
| Your own themes | No | Yes |
A limit stops you adding past it. It never takes away what you already have: if you finish the trial with ten sessions, all ten stay readable, exportable and deletable, and the one you are playing keeps saving.
Open Licence in the rail on the home screen, paste the key from your purchase email, and press Activate. It is one payment, with no subscription. The licence is checked online once to activate, and again about once a week.
If the computer is offline when a check is due, nothing happens: the licence keeps working for 30 days from the last successful check, with a reminder in the last week. Only after that does it fall back to the free version, and it returns the moment you reconnect.
A key can be active on a limited number of computers. On the old one, open Licence and press Deactivate on this computer, then enter the key on the new one. To bring your sessions, journal and data with you, use Back up everything on the Help page and Restore from a backup on the new computer. If you no longer have access to the old computer, write to support and the activation is removed by hand.
A small circled i beside a label explains it. Hover it, or move to it with the keyboard, and a short note opens; move away and it closes.
The screens keep their own wording short for this reason: once you know what a field does you do not need to read about it again. The icon is there for the times you do. Clicking away or pressing Esc closes it. Screen readers read the explanation along with the label, so nothing is hidden from them.
Each note is the short form of a page in this guide. Where a note ends with a topic name, that page has the whole story.
Changes outside the script editor, newest first.
W1 and MN1 are in the timeframe row with the rest, for the main chart and context panes. A week runs from Monday 00:00 and a month from the 1st, on your data's clock. Switching to one shows the last week or month that has closed, never one still forming, so nothing ahead is given away; switching back to a finer timeframe returns you to exactly where you were.
Right-click the chart and choose Add price alert at the price under the mouse. A dashed line with a bell marks it. When a bar touches it, wick or gap, playback stops and a band says so, with Keep it armed or Remove it. Each alert is listed in the right-click menu and in Objects with its own remove button; Remove all is there too. Alerts are saved with the session.
Drag an alert's line up or down to move it; it is armed again where you let go. With one of the session and ICT tools on the chart, right-click on or near one of its levels and the menu also offers an alert right on it, named after it: Add price alert at PDH 1.08500. That covers the previous day's, week's and month's highs and lows, the ADR levels, the high and low of each session's and kill zone's latest range, the edges of open fair value gaps and live order blocks, and equal highs and lows not yet swept. The name shows on the line and in the notice when it is touched. The alert stays at that price: it does not follow the level to the next day.
A new install opens with DEMO/USD, a practice market the app generates, and a Try it now button. The import wizard gained a guided Dukascopy route that sets the clock from the file, and a file can now be added to an existing dataset. See Bringing in price data.
Right-click the chart and tick Trading sessions to shade Sydney, Tokyo, London and New York behind the candles, each in its own city's hours with daylight saving followed, and the overlaps in a colour of their own. See Trading sessions on the chart.
The built-in themes draw candles in black and white with outlines, and Reset to defaults brings them back. The bid and ask lines have colours of their own, following the candles until you change them.
Buy is bright blue and Sell bright red, the toolbar's icons and labels are white, and the colour palette has ten shades per colour on round swatches.
A Data page lists and deletes datasets. A Help page sends feedback with diagnostics, backs up everything to a folder and restores it, and lists the open-source notices. The app now keeps a small error log on this computer.
A session's results can be saved as a picture to share. A replayed trade shows the original beside your retake. See Sessions and Replaying a trade.
A first run opens in Ink. On any button or badge filled with a bright colour, such as Ink's amber, the text is now black instead of white, and the app picks black or white for each theme from how bright the colour is, so it stays readable in every theme and in your own. Your saved theme is not changed.
A step now sends the chart only the new candle instead of the whole history, so 5× and 10× reach 5 and 10 candles a second on a normal session. See Playing, stepping and speed.
The app, this guide and the MetaTrader exporter now carry the one name. Files the old exporter wrote are still found and imported.
Everything is on for 14 days. After that the free version keeps replay, orders, drawings, the journal and your own data, and limits saved sessions, indicators, scripts, prop firm challenges, more instruments in a session, extra chart panes and your own themes. One payment unlocks the rest. See The trial, the free version and your licence.
The speeds are 1, 2, 3, 5 and 10 candles per second, on a slider you can click, drag or use from the keyboard. One candle a second is slow enough to follow closely. See Playing, stepping and speed.
The importer reads epoch times (Binance, TradingView), Dukascopy's day-first dates, US month-first dates, decimal commas and times stated with a zone. It refuses dates that do not exist, tells you when a date could be read two ways, and warns about bars that are not one minute. You can pick the date format yourself. See Bringing in price data.
The instrument list now includes NASDAQ and US500 CFDs, the main futures, shares and crypto, with sensible contract sizes. A lot is one contract for futures and one share for stocks.
Replay only New York, or London and New York, and skip the rest. See Play only some sessions.
A buy is always blue and a sell always orange, whatever theme you use. Before, a theme with an amber accent painted buys the same colour as sells.
The name sits beside the logo in the replay window, Exit session is set apart and white, the dashboard's market-session bars fill their space, and toolbar icons with a drop-down line up with the rest and light up only on the icon. Small i icons now explain things in place; see The info icons.
Each dataset now records which time zone its times are in, chosen when you import it (Kenya by default) and correctable later. Market sessions, hours and weekdays use it, and the Dashboard gained a Trades by market session chart. See Data and the clock.
Indicators, Objects, Chart layout and Properties are now full-height edge panels that leave the chart live; small dialogs share one look; the order ticket, right-click menu and indicator settings follow it. See Panels and menus on the chart.
A Themes page defines every colour: five built-in themes, your own copies, a legibility check, import and export. See Themes.
The profit target, maximum loss and daily loss limit are now typed as a percentage of the account, the way firms state them, with the dollar amount shown beside the field. The Simulation tab's challenge test works the same way. See Starting a challenge.
Home, Sessions, Trades, Analytics and Prop firm now run to the window's edges: an icon rail, a header, a bar of controls and two panes that scroll on their own, instead of cards centred on a page. The analytics filters moved from pop-up menus to a pane down the left. See The home screen.
Analytics now fills the replay window corner to corner, and the triangle in its corner moves it: to the left or right of the chart, below it, or out into a window of its own that can sit on another monitor. See Docking and windows.
Dashboard, Sessions, Trades, Analytics and Prop firm pages replace the plain session list. Sessions now carry a strategy, a description, time invested and how far the replay has gone. See The home screen.
A panel beside the chart with a trade list, a calendar, session notes and a note editor per trade with automatic entry and exit screenshots, tags, an execution grade, a checklist and templates. The old statistics pop-up moved into analytics. See Opening the journal.
Filters, five tabs (Performance, Time, Drawdown, Journal, Simulation), CSV export, and a Monte Carlo simulation. See Analytics.
A daily loss limit with a configurable basis and reset time, minimum trading days, a time limit, a consistency rule, lot, weekend and news rules, presets, a live HUD with a rules report, no-rewind, and a pass-odds estimate. See Prop firm.
IBM Plex is now bundled with the app (it was named in the theme but never loaded, so the app was using system fonts), with a new icon set, tinted fills and hand-drawn charts that have tooltips and a “show the numbers” table.
A journal that sits beside the chart, so you can write about a trade while its bar is still on screen. Start with a topic below or search (Ctrl K).
Press the Journal button in the toolbar, next to Indicators, or press J.
The journal opens as a panel on the right and the chart makes room; it does not cover it. A small number on the button counts closed trades you have not marked as reviewed. It has three tabs: Trades, Calendar and Session.
Closed trades, newest first. Each row has a coloured arrow for buy or sell, the symbol and lots, when it closed, its R, its grade if you gave one, small icons for notes and screenshots, a green tick when reviewed, and the profit or loss.
The camera at the top of the panel turns auto screenshots on or off. While it is on, a picture of the chart is taken when a position opens and another when its trade closes, and both are attached to that trade's entry. Pictures are shrunk to at most 1280 pixels wide and stored as JPEG, so a screenshot is about 100 KB. Trades already open when you resume a session are not captured.
Open a trade and you get the facts, your screenshots, a note, and a short review.
Entry and exit price, how long it was held and in how many bars, the result in R, and two figures you will not find elsewhere: Max against (how far price went against the trade before it closed) and Max for (the best it ever got), both in R. They come from the replay's own price path, so they are exact. If the stop and target were both inside one minute, a note says the stop was assumed to fill first.
Three buttons: Replay this trade starts a new session at its entry with the future hidden, Capture chart takes a screenshot now, and Copy notes puts the entry on the clipboard as Markdown.
Click and type. A new line starts a new block. Type / at the start of a line for a menu:
| Command | What it adds |
|---|---|
| Text, Heading, Bulleted list, To-do, Quote | A block of that kind. |
| Divider | A horizontal rule. |
| Screenshot | A picture of the chart as it is now, inline. |
| Template: ... | A prepared outline: Trade plan or Trade review. |
Typing # , - , [] or > at the start of a line turns it into a heading, bullet, to-do or quote. Enter on an empty bullet or to-do ends the list. Backspace at the start of a block turns it back into text. Pasting text pastes plain text, and pasting an image from the clipboard attaches it as a screenshot. Up and down arrows move between blocks.
See Tags, templates and checklists. The Delete this entry and its screenshots link at the bottom removes everything written about the trade.
Two more tabs in the journal panel.
A month grid starting on Monday. A day with trades shows its net result and trade count, tinted green or red, stronger the bigger the day. Click a day to list its trades, and click one to open its entry. Year shows twelve months with their results; click a month to go to it. Days are the dates of the replayed market, not the dates you practised.
Here you set the session's strategy and description, and write session notes in the same editor, for a daily review or a lesson that belongs to no single trade. It also shows the session's net profit and loss, win rate and expectancy, how long you have spent in it, and a button that opens analytics.
Tags only help if you use them the same way every time, so they come in a few fixed groups with a few values each.
The starting groups are Setup (breakout, pullback, reversal, range fade, trend continuation), Market condition, Emotion and Mistake. Click a tag to apply it. Add beside a group makes a new tag in it, New tag group makes a group, and Delete tags shows a bin beside each tag. Tags are shared by every session, so a tag means the same thing wherever you analyse it.
The default Pre-trade checklist has six items (higher-timeframe bias, entering at the planned level, stop placed first, risk within limit, reward-to-risk at least 1.5, no news soon). Tick what you did. Analytics then compares trades where you followed at least 80% of it with the rest.
Insert Trade plan or Trade review with / in the note editor.
Every figure that groups by a tag shows how many trades is behind it, and pale rows have fewer than five. Treat anything with a handful of trades as a hint, not a finding.
Everything stays on your computer.
Deleting a session deletes its screenshots with it. A session saved by an older version opens as before, with an empty journal.
What your closed trades say about how you trade, across every session. Start with a topic below or search (Ctrl K).
Press Analytics in the toolbar (or A) during a session, or use the Analytics page on the home screen. In a session it fills the window corner to corner and can be docked or popped out.
It reads every saved session and, when you open it mid-session, the live state of the open one, and it follows your trades as they close. Reopening it puts you back on the tab and filters you left.
The tabs are across the top with Filters, Copy summary and Export CSV at the right. The filters are a pane down the left. In a narrow dock or window the pane starts closed and the buttons shrink to icons; Filters opens the pane either way.
| Filter | Narrows to |
|---|---|
| Type | Backtesting or prop firm sessions. |
| Assets, Side, Outcome | Symbols, buys or sells, wins, losses or breakeven. |
| Tags, Grade | Trades carrying any chosen tag, or given a grade. |
| Session, Strategy | Particular sessions or strategies. |
| Market session | Asia, London, New York or off-hours, by entry time, in real market time. The pane shows each session's hours on your data's clock beside it; see Data and the clock. |
| Day, Time | Weekdays, and an entry-time window that may cross midnight. |
| Timezone | What day and hour mean. The default is the data's own clock (Kenya, for data exported in Kenyan time); choose UTC, New York, London and others to read the same trades on another clock. |
| Date | A range of the replay's market dates, by close time. |
Within one filter the choices are any of; across filters they are all of. Chips at the top of the pane show what is narrowing the figures, each with an ×, and Clear all removes them all. A group with something chosen opens by itself and shows how many.
A trade whose result is within a set number of dollars of zero is breakeven, not a win or a loss. The threshold is on the Performance tab, beside the Breakeven figure, and is $0 until you change it.
Export CSV saves the trades behind the current filters, one row each, with R, excursions, grade and tags. Copy summary puts three lines of headline figures on the clipboard.
Under each chart, Show the numbers swaps it for a table of the same data. Hover a chart for exact values.
The small triangle in analytics' top-right corner moves it. Click it and pick a place.
| Place | What you get |
|---|---|
| Full window | Corner to corner over everything under the title bar. The default. Esc closes it. |
| Left of the chart | A column beside the chart, chart on the right. Drag its edge to resize. |
| Right of the chart | The same on the other side, next to the journal if that is open. |
| Below the chart | A panel along the bottom. Drag its top edge to resize. |
The chart always keeps some room, and the place and sizes you choose are remembered. Docked beside the chart, analytics updates as you close trades, so you can watch the figures move while you trade.
Pop out to a window opens analytics as a second window that you can drag anywhere, including to another monitor. With more than one monitor connected the menu also lists Fill monitor 1, 2… (with each one's size, and which is your main display and which has the replay), which opens it maximised on that display.
Pop out still works; the menu just has no monitor list. Drag the window's title bar to place it.
The first tab.
A figure that cannot be worked out shows a dash, with the reason beside it (no stop-loss, no losing trades). You will never see “infinity” or a zero standing in for “unknown”. Trades without a stop-loss have no R and are left out of R figures, with a count of how many.
When you win, and when you do not.
Pale bars and cells have fewer than five trades behind them. Hover for the exact count.
The cost of the ride.
Where what you wrote meets what happened.
Your closed trades, drawn at random thousands of times.
Each simulated run takes trades from your own results, either drawing with replacement or reordering the same trades, and builds a balance curve. The chart shows the median and the 25th–75th and 5th–95th percentile bands. Beside it: the chance of finishing in profit, the typical and 5%/95% final balance, the typical worst drawdown, and the chance of losing half.
Tick Test against a challenge to give it a profit target and a maximum loss, each as a percentage of the starting balance with the dollar amount beside it (the loss fixed, or trailing from the equity high). It then reports the chance of passing and of breaching first.
The definitions, so you can check any figure by hand.
| Figure | Definition |
|---|---|
| Net P&L | Profit after spread and commission. |
| Win rate | Winning trades ÷ all closed trades, breakeven included. |
| Expectancy | Average net result per trade. The R version is the mean R over trades that had a stop-loss. |
| Profit factor | Gross profit ÷ gross loss. With profit and no loss it says so instead of printing infinity. |
| Payoff ratio | Average win ÷ average loss. |
| R | Net result ÷ the risk to the stop-loss at entry, costs included. No stop-loss, no R. |
| Planned R | Distance to the take-profit ÷ distance to the stop, from the bracket live at close. |
| Max against / for | Maximum adverse and favourable excursion in R, from the replay's price path. |
| Balance and drawdown | Closed-trade balance. One curve feeds the profit chart, the balance figure and the drawdown, so they cannot disagree. A drawdown runs from a peak until the peak is regained. |
| Combined balance | With several sessions in view, the sum of their starting balances. |
| Trade frequency | Trades ÷ the days, weeks and months in which you traded, so a quiet month does not drag it down. |
Sample sizes are shown beside grouped figures (n=4) and thin groups are drawn paler. The app does not compute Sharpe, Sortino or Kelly: on the small samples a replay produces they are noise dressed as precision.
Trade under a firm's rules, watch the room you have left, and see whether you would have passed. Start with a topic below or search (Ctrl K).
Choose Prop firm challenge in the new session form. Pick a shape, change anything, start.
Each preset sets its numbers as a share of your starting balance, so it fits any account size. Two-phase, phase 1 (10% target, 5% daily and 10% total loss, 4 days, 30 days), phase 2 (5% target), One-step (10% target, 3% daily, 6% trailing end-of-day loss), Futures-style trailing (6% target, 4% trailing loss that locks at the start balance) and Drawdown only.
These are common shapes, deliberately not named after real firms. Rules change, and an out-of-date preset would be worse than none. Copy the numbers from your firm's own page before an attempt that matters.
| Rule | Meaning |
|---|---|
| Profit target | Profit above the starting balance that passes the challenge, as a percentage of the account. Blank means no target. |
| Maximum loss and floor | How far you can fall (a percentage of the account), and how the floor moves: static, trailing intraday, trailing end of day or trailing, locks at start. |
| Daily loss limit | Loss allowed within one trading day (a percentage of the account), measured from the day's opening balance, opening equity, or the higher of the two. |
| Day resets at | The UTC time a trading day rolls over. Many firms use broker server time, often 22:00 UTC. |
| Minimum trading days | Separate days you must trade before the target counts. |
| Time limit | Calendar days from the first bar. |
| Best day at most | The consistency rule: no one day above this share of your profit. |
| Largest position | Lots. |
| No weekend holding | No position open across Saturday midnight (UTC). |
| News blackout | No trade opened or closed within this many minutes of a high-impact event. Needs a calendar imported with the data. |
| Your profit split | The share you keep, used for the payout estimate. |
| No rewinding | Time only moves forward. Stepping back, jumping to an earlier date and Home are disabled. |
Editing any field by hand switches the preset off (Reset to the preset brings it back). The profit target, maximum loss and daily loss limit are typed as percentages of the starting balance, and the dollar amount they come to is shown beside each, so “10” is never ambiguous. Change the balance and the dollars follow; the percentages stay.
A strip in the title bar shows the room you have left under each limit, on every bar.
A bar turns amber when half the room is gone and red when a quarter is left. It is a gauge you can watch approach zero rather than a pop-up after the fact.
The drawdown limit and the daily loss limit end it the moment equity touches them, floating profit and loss included, and the strip says BREACHED or DAILY LOSS BREACHED. Once breached an attempt stays breached. A rule judged on your closed trades (time limit, consistency, lot size, weekends, news) turns the strip to RULE BROKEN. Reaching the target with every rule satisfied shows PASSED.
Click the strip for a card listing every rule with a tick, cross or flag, a line of detail, and progress bars for the target and trading days. It also shows closed profit, days traded, and what you would keep at your profit split. The consistency rule is shown as information until you reach the target, which is when firms judge it.
“Maximum drawdown” means four different things at four different firms.
| Floor | Moves how |
|---|---|
| Static | Fixed at the starting balance minus the limit. Never moves. |
| Trailing, intraday | Follows your highest equity continuously, floating profit included, so it can rise during a trade. |
| Trailing, end of day | Follows only your best end-of-day equity. Swings within a day do not move it until the day rolls over. |
| Trailing, locks at start | Trails like intraday until the floor reaches the starting balance, then stays there for good. |
“End of day” is measured on equity (floating included) at the reset time, the more common convention. If your firm uses closed balance instead, treat this as slightly stricter than yours.
The fifth page on the home screen.
The odds estimate ignores the daily loss limit and the other rules, and it is only as good as the number of trades behind it. It is a measure of your edge, not a promise.
Write your own indicators in the Pine Editor, put them on the chart, and keep them between sessions. Start with a topic below or search (Ctrl K).
The Pine Editor lets you write your own indicators and put them on the chart next to the built-in ones.
Open the Pine Editor tab in the bottom dock, write a script, and press Ctrl Enter. It appears on the chart like any other indicator, with a gear for its settings. Ctrl S saves it to your library.
The language is Pine-like. It is a subset of TradingView's Pine Script v5 that runs offline, inside this app, on the candles the replay has reached. Plenty of Pine indicators paste in and run, and many will not. When something is not supported, the editor tells you which line and what, instead of drawing something different. “Pine Script” is TradingView's trademark, and nothing here is affiliated with it.
There is no strategy(). This app is for manual trading, and a script that placed orders would bypass the fill rules. Write an indicator() and make the decisions yourself.
Follow Your first script to run something in two minutes, then read How a script runs to know why a script cannot peek at the future.
Run the EMA Cross example, change one setting, and save it. It takes about two minutes.
The indicators on your chart are kept when you close the app and come back when it starts. See Saving and restoring.
//@version=5
indicator("EMA Cross", overlay = true)
fastLen = input.int(9, "Fast length", minval = 1)
slowLen = input.int(21, "Slow length", minval = 2)
fast = ta.ema(close, fastLen)
slow = ta.ema(close, slowLen)
plot(fast, "Fast", color.blue, 2)
plot(slow, "Slow", color.orange, 2)
plotshape(ta.crossover(fast, slow), "Buy", shape.triangleup, location.belowbar, color.green)
//@version=5 says which language version the script is written for.indicator(..., overlay = true) names the indicator and puts it on the price chart. Without overlay it gets a pane of its own.input.int(...) makes a setting. It shows up in the gear with the title you give it.ta.ema(close, fastLen) is a moving average of the close. The script runs once per bar, so fast is a different number on every bar.plot(...) draws a series. plotshape(...) draws a marker on the bars where its condition is true.Press New for an empty script, or paste one from elsewhere. Type a name and press Ctrl Space for the list of functions with their parameters. If a script has unsaved changes, the editor asks before it lets you open another.
A script runs one bar at a time, from the oldest bar to the newest, on the candles the replay has reached.
A bar's value cannot depend on a later bar, because later bars are not there. That is what makes a replay honest: the indicator shows what it would have shown at that moment. On a higher-timeframe pane the newest bar is the partial one, so it grows as you step.
barstate.islast, last_bar_index and timenow mean “the bar at the cursor”. A script that reads them is re-run on every step instead of extended. That is correct, only slower.A ta.* call inside an if or inside barstate.islast still sees every bar. When its arguments depend on nothing but the bar (series, constants, inputs), the bars it skipped are fed through first. So this works:
if barstate.islast
hh = ta.highest(high, 30)
line.new(bar_index - 30, hh, bar_index, hh)
If an argument is a variable that changes from bar to bar, the skipped bars cannot be replayed, and the call sees only the bars it ran on. Move the call out of the if and use its result inside.
There is no wall clock, and math.random() is refused. A script always gives the same output for the same candles. A loop that never ends is stopped after 500,000 iterations on one bar, with an error on the line.
The syntax follows Pine v5. Indentation matters: four spaces or a tab.
x = 1 // declare
x := x + 1 // change (also += -= *= /= %=)
var total = 0.0 // keeps its value from bar to bar
float f = na // type words are accepted and ignored
[a, b, c] = ta.macd(close, 12, 26, 9) // tuples
prev = close[1] // earlier bars: close[0] is now
ta.sma(close, 5)[1] // works on any expression
if close > open
plot(close)
else if close < open
plot(open)
for i = 0 to 9 by 2 // inclusive; break / continue work
while cond
switch kind // or a bare `switch` on conditions
"A" => 1
=> 0
f(a, b = 2) => a * b // functions; a body can be indented
Every variable is a series: it has a value on every bar. close is today's close, close[1] is the previous bar's, and close[20] is twenty bars back. The first bars of a long lookback have no value yet, which shows as na (a gap on the chart).
A plain total = 0.0 starts again on every bar. var total = 0.0 is set once and keeps whatever you assign to it, so you can count, accumulate, or remember a level.
+ - * / %, == != < > <= >=, and or not, and ? :. Strings join with +.
na.na is false, except !=, which is true (as with NaN). So ta.change(x) != 0 is true on the first bar.A statement can continue on the next line if you indent it by something that is not a multiple of four, or end the line with an operator or comma.
Candles are stored as integer points, and a script sees close = 1.08432. Float arithmetic can make two moves that are exactly equal in points differ in the last digit.
Do not compare computed prices with ==. Compare against a tolerance instead: math.abs(a - b) < syminfo.mintick / 2. The built-in ta.dmi already does this.
Anything you declare with input.* appears in the indicator's gear, where you can change it without touching the code.
len = input.int(14, "Length", minval = 1, maxval = 200, group = "Main")
src = input.source(close, "Source")
mode = input.string("EMA", "Type", options = ["EMA", "SMA"])
tf = input.timeframe("D", "Timeframe")
sess = input.session("0800-1600", "Session")
| Function | Setting shown |
|---|---|
input.int, input.float | A number. minval, maxval and step limit it. |
input.bool | A switch. |
input.string | Text, or a dropdown when you pass options. |
input.color | A colour picker. |
input.source | A choice of price series (close, open, hl2, ...). |
input.timeframe | A timeframe dropdown. |
input.session | A session such as 0800-1600. |
input.enum | A dropdown of an enum's values, shown by their titles. See Enums. |
input(...) | The plain form picks the kind from the default you give. |
group and title may be a string kept in a variable (G = "Main"), so a group name can be written once.Type a name in the editor and press Ctrl Space for the full list with parameters. The list is generated from the same tables the compiler uses, so it never drifts.
open high low close volume time hl2 hlc3 ohlc4 hlcc4 bar_indexyear month dayofmonth dayofweek weekofyear hour minute second (UTC; dayofweek is 1 = Sunday)ta.tr ta.obv ta.accdist ta.pvt ta.nvi ta.pvi ta.wad ta.wvad ta.iii, ta.vwap / vwap (restarts each UTC day)time_close, last_bar_timesyminfo.ticker / mintick / pointvalue / currency / pricescale / basecurrencytimeframe.period / multiplier / isintraday / isdailybarstate.isfirst / islast / isconfirmed ...chart.bg_color / fg_color and the chart.is_* flagsMoving averages: sma ema rma wma vwma swma hma alma linreg median mode cog.
Oscillators: rsi cci mfi cmo tsi wpr stoch macd.
Bands and ranges: bb bbw kc kcw atr tr dmi sar supertrend.
Statistics: stdev variance dev highest lowest highestbars lowestbars range max min change mom roc percentrank percentile_nearest_rank percentile_linear_interpolation correlation.
Events: crossover crossunder cross rising falling cum valuewhen barssince pivothigh pivotlow.
The older bare names (sma, rsi, ...) still work.
abs ceil floor round sqrt pow log log10 exp sign sin cos tan asin acos atan todegrees toradians max min avg sum round_to_mintick, and pi e phi.tostring tonumber length upper lower contains startswith endswith substring replace_all format trim repeat pos split format_time. str.tostring accepts format.mintick, format.percent, format.volume and patterns such as "#.#####".new rgb from_gradient r g b t and the named colours.year(time) month(time) ... timestamp(y, m, d, h, mi), all UTC.na nz fixnan int float booltime(timeframe, session) is the bar's timeframe start ("D", "W" starting Monday, "M", "60"), or na when the bar is outside session. Sessions look like "0800-1600", "2200-0600", or "0800-1600:23456" for chosen days (1 = Sunday), all in UTC. timeframe.change("D") is true on the first bar of each new day, and timeframe.in_seconds("60") converts a timeframe.
ta.ema starts from the first value (like Pine). The built-in EMA indicator starts from an SMA. The two agree after a few hundred bars.ta.pivothigh and ta.pivotlow report a pivot only once its right-hand bars exist (rightbars late), so they never see ahead. Plot with offset = -rightbars to put the mark on the pivot bar.ta.kc uses an EMA of the true range. The built-in Keltner indicator uses ATR.A script draws by calling output functions. Each call adds one thing to the chart.
| Call | What it does |
|---|---|
plot(series, title, color, linewidth, style, histbase, offset, display) | Styles: line, stepline, histogram, columns, circles, cross, area, linebr, areabr. color can change per bar. display = display.none hides it, and fill can still use it. |
hline(price, title, color, linestyle, linewidth) | A horizontal level. |
fill(plot1, plot2, color) | Shades between two plots, or a plot and an hline. |
bgcolor(color) | Stripes behind the candles. |
barcolor(color) | Recolours the candle (body, border and wick) on that bar. na leaves it alone. When several scripts colour the same bar, the later one wins. |
plotshape(cond, title, style, location, color, text, size), plotchar | Markers above or below candles. |
plotarrow(series, colorup, colordown) | An up arrow under the bar where the series is above 0, a down arrow over it where below 0. The arrow size does not scale with the value. |
plotcandle(open, high, low, close, title, color, wickcolor, bordercolor), plotbar(...) | A candle or OHLC-bar series of the script's own, on the price chart (overlay = true) or in its own pane. A bar with any na price is skipped. |
indicator(title, shorttitle, overlay) | overlay = true draws on the price chart. Otherwise the script gets a pane of its own. |
A script with indicator(overlay = false) and no plot has nothing to put in a pane, so its markers and stripes go on the price chart.
//@version=5
indicator("Inside Bar", overlay = true)
inside = high < high[1] and low > low[1]
bgcolor(inside ? color.new(color.yellow, 80) : na)
plotshape(inside, "Inside bar", shape.circle, location.abovebar, color.yellow, size = size.tiny)
Pass na to a colour argument to draw nothing on that bar. color.new(color, 80) makes a colour 80% transparent.
//@version=5
indicator("Heikin Ashi")
haClose = (open + high + low + close) / 4
var haOpen = na
haOpen := na(haOpen[1]) ? (open + close) / 2 : (haOpen[1] + haClose[1]) / 2
haHigh = math.max(high, math.max(haOpen, haClose))
haLow = math.min(low, math.min(haOpen, haClose))
plotcandle(haOpen, haHigh, haLow, haClose, "Heikin Ashi", color = haClose >= haOpen ? color.teal : color.red)
request.security runs an expression on a higher timeframe and hands the result back to each chart bar.
//@version=5
indicator("Daily EMA", overlay = true)
tf = input.timeframe("D", "Timeframe")
daily = request.security(syminfo.tickerid, tf, ta.ema(close, 20))
plot(daily, "Daily EMA", color.orange, 2, plot.style_stepline)
The call is request.security(symbol, timeframe, expression, gaps). It regroups the chart's own bars into the higher timeframe and runs expression on the result.
A higher-timeframe bar is shown from the chart bar that ends it onwards. Until then you keep seeing the previous closed one. On an M5 chart, "60" shows the previous hour for the first eleven bars of an hour, and the new hour from its last bar.
A script cannot see a bar that has not finished, so replay and a full run give the same picture and nothing repaints. This is why lookahead_on is refused. You do not need the close[1] plus lookahead_on trick: the default already never looks ahead.
The stepped line is the correct output, not a glitch. A daily EMA changes once a day, so on a lower timeframe it moves in steps.
"15", "240"), days ("D", "3D"), or "" for the chart's own. It must be a whole multiple of the chart's timeframe and not lower. Weekly and monthly are not offered, because the app's bars stop at one day. Days are UTC days.syminfo.tickerid, syminfo.ticker, or ""). Other symbols are not available, and neither is request.security_lower_tf. A script is given the chart's own bars, so there is no finer data to ask for.gaps = barmerge.gaps_on returns na except on the chart bar where a higher-timeframe bar closed.[a, b] = request.security(..., [ta.rsi(close, 14), high]).request.security.Variables used in the expression are recomputed on the higher timeframe from their definition, as Pine does. m = ta.ema(src, len) then request.security(..., m) works, and so do inputs. A variable that is changed with :=, declared with var, or a function that reads script variables cannot be recomputed. The error says so. Put the calculation inside the expression instead.
An array holds a list of values. Use one to keep a rolling window, a set of levels, or anything you build up bar by bar.
//@version=5
indicator("Median of the last 9 closes", overlay = true)
var window = array.new_float(0)
window.push(close)
if window.size() > 9
window.shift()
plot(window.median(), "Median", color.orange)
array.new_float / new_int / new_bool / new_string / new_color(size, initial), array.new<float>(...), and array.from(a, b, ...). Typed declarations (array<float> a = ..., float[] a = ...) are accepted.
Every function can be called either way: array.push(a, x) or a.push(x), including on an expression, as in array.from(x, y).max().
size get set first last includes indexof lastindexofpush pop shift unshift insert remove clear fill reverse sort(order.descending) concatcopy slice(from, to) fromna) sum avg min(nth) max(nth) median range variance stdevjoin(separator), and for item in a or for [i, item] in a to loopa = array.new_float(0) is a new, empty array on every bar. Use var to keep one across bars.pop, shift, first or last on an empty one, stops the script with an error on that line, as in Pine.a[i] is not element access. It is the history operator, so use a.get(i).A map looks values up by key. A matrix is a grid of rows and columns.
//@version=5
indicator("Up / down days")
var tally = map.new<string, int>()
key = close > open ? "up" : "down"
tally.put(key, nz(tally.get(key)) + 1)
plot(nz(tally.get("up")), "Up", color.green)
plot(nz(tally.get("down")), "Down", color.red)
map.new<string, float>() with put get contains remove clear size keys values copy put_all.
put and remove give back the old value, or na. get gives na for a missing key.na.for [key, value] in m loops over entries in the order they were added.matrix.new<float>(rows, columns, initial) with get set rows columns elements_count row col add_row add_col remove_row remove_col swap_rows swap_columns fill copy transpose reshape sum diff mult det inv trace avg min max.
mult takes a matrix, an array (matrix times vector, gives an array) or a number.sum and diff take a matrix of the same size, or a number.inv of a singular matrix is an error.Method calls work on everything that has a collection or drawing object: m.put(k, v), x.get(r, c), tbl.cell(0, 0, "x"), lbl.set_text("y"). When a variable was created by table.new, map.new, array.new... and so on, the call is checked with that type's argument names. Otherwise the function is chosen from the value when it runs, with positional arguments only.
A type groups related values into one object. A method is a function you call on a value with a dot.
//@version=5
indicator("Swing tracker", overlay = true)
type Swing
float price
int bar
bool isHigh = true
method distance(Swing this, float from) =>
math.abs(this.price - from)
var last = Swing.new(high, bar_index)
if ta.pivothigh(high, 5, 5) == high[5]
last := Swing.new(high[5], bar_index - 5)
plot(last.distance(close), "Distance to last swing high")
type Name is followed by an indented list of fields: [type] name [= default]. Name.new(a, b) or Name.new(price = 1.0) makes an object. A field left out takes its default, else na ("" for a string, false for a bool).
p.price reads a field. p.price := 2 and p.count += 1 change it.
Objects are shared, not copied, when assigned: b = a is the same object. a.copy() (or Name.copy(a)) is a new one with the same fields. An object can hold other objects, arrays, maps and so on.
method name(Type this, ...) => is called as x.name(...). Two methods can share a name for different types, and a method can be defined on float, string and so on, as in close.double(). A built-in method of the same name (a.size() on an array) still works on its own types.
na object stops the script with an error on that line.p[1] is the object the variable held on the previous bar. On the first bar there is none, so guard it: bar_index > 0 ? p[1].x : na.An enum is a fixed set of named values. Use one when a script has a few modes, and you want the settings gear to offer them as a dropdown.
//@version=5
indicator("Average type", overlay = true)
enum Kind
ema = "Exponential"
sma = "Simple"
wma
kind = input.enum(Kind.ema, "Average")
len = input.int(20, "Length")
avg = switch kind
Kind.ema => ta.ema(close, len)
Kind.sma => ta.sma(close, len)
=> ta.wma(close, len)
plot(avg, "Average")
enum Name is followed by an indented list of values. Each is a name, or a name with a title in quotes: sma = "Simple". The title is what people see in the dropdown and what str.tostring gives. Without a title, the name is used. An enum is defined at the top level of the script.
Kind.ema.== and !=, or choose with switch, as above.input.enum(default, title, options) makes the dropdown. Pass options = [Kind.ema, Kind.sma] to offer only some values. The default has to be one of them.The saved setting is the value's name, so renaming a value in the script resets that setting to the default.
A library is a script that holds functions, types and enums for other scripts to use. It draws nothing and cannot be added to the chart.
Begin the script with library("Name"), then define things. Put export before each one other scripts should be able to use.
//@version=5
library("MyMath")
sq(float x) =>
x * x
export hyp(float a, float b) =>
math.sqrt(sq(a) + sq(b))
export type Pair
float a
float b = 2
export enum Side
buy = "Buy"
sell
Press Save. The editor checks a library as you type, and Add to chart tells you it is a library instead of adding it.
//@version=5
indicator("Hypotenuse")
import me/MyMath/1 as m
p = m.Pair.new(3, 4)
plot(m.hyp(p.a, p.b))
import author/Name/version as alias finds the saved script whose library() title is Name. Case does not matter, and the author and version are ignored, so import MyMath as m works too. Everything exported is reached through the alias: m.hyp(...), m.Pair.new(...), m.Side.buy.
library(), imports, and function, method, type and enum definitions.export are private to the library (sq above).import line, with the library's own line number in the message.These draw objects at a bar and a price, rather than a value on every bar.
//@version=5
indicator("Range box", overlay = true)
var bx = box.new(bar_index, high, bar_index + 20, low, border_color = color.teal, bgcolor = color.new(color.teal, 85))
if barstate.islast
label.new(bar_index, high, "last", style = label.style_label_down, color = color.red)
line.new(bar_index - 20, close, bar_index, close, extend = extend.right, style = line.style_dashed)
label.new(x, y, text, xloc, yloc, color, style, textcolor, size, ...)line.new(x1, y1, x2, y2, xloc, extend, color, style, width)box.new(left, top, right, bottom, border_color, border_width, border_style, extend, xloc, bgcolor, text, ...)linefill.new(line1, line2, color) shades the area between two lines. It disappears when either line is deleted. linefill.delete and set_color are available.polyline.new(points, curved, closed, xloc, line_color, fill_color, line_style, line_width) draws through an array of points. See below.Each has set_* and get_* functions (label.set_xy, line.set_xy2, box.set_rightbottom, line.get_price, ...) and delete.
x is a bar index by default. With xloc.bar_time it is a time in milliseconds. It may be in the future (bar_index + 10).yloc.abovebar and yloc.belowbar put a label just above the bar's high or below its low, ignoring y.label_up/down/left/right/center, the four corner styles, none, and the shape styles (triangleup, circle, ...).A polyline connects many points with one line, and can fill the shape. Make the points with chart.point.
//@version=5
indicator("Triangle", overlay = true)
if barstate.islast
pts = array.new<chart.point>()
pts.push(chart.point.from_index(bar_index - 20, low))
pts.push(chart.point.from_index(bar_index - 10, high))
pts.push(chart.point.from_index(bar_index, low))
polyline.new(pts, closed = true, line_color = color.orange, fill_color = color.new(color.orange, 85))
chart.point.from_index(index, price), chart.point.from_time(time, price), chart.point.new(time, index, price) and chart.point.now(price). A point has .index, .time and .price.curved = true bends the line smoothly through the points. closed = true joins the last point to the first. fill_color shades the inside.xloc = xloc.bar_time the points are placed by their time, not their index.polyline.delete(id) removes it.max_polylines_count.A plain l = line.new(...) makes a new line on every bar. To keep one and move it, declare it with var. To keep only the latest, call line.delete(l) on the old one first.
Past the limit, the oldest is deleted, as in Pine. The limit is 50 of each kind by default. Raise it up to 500 with max_lines_count, max_labels_count or max_boxes_count in indicator(...).
delete on na does nothing. On anything that is not that kind of object, it is an error.tooltip shows when you hover the label.A table is a panel of cells fixed to a corner of the pane. It is the place for a small summary that should stay put while the chart scrolls.
//@version=5
indicator("Info panel", overlay = true)
var t = table.new(position.top_right, 2, 3, border_color = color.gray, border_width = 1)
table.cell(t, 0, 0, "Panel", text_color = color.white, bgcolor = color.blue)
table.merge_cells(t, 0, 0, 1, 0)
table.cell(t, 0, 1, "Close", text_color = color.white, text_halign = text.align_left)
table.cell(t, 1, 1, str.tostring(close, format.mintick), text_color = color.green, tooltip = "the last close")
table.new(position, columns, rows, bgcolor, frame_color, frame_width, border_color, border_width)table.cell(table, column, row, text, width, height, text_color, text_halign, text_valign, text_size, bgcolor, tooltip)table.cell_set_*, table.set_*, table.merge_cells, table.clear, table.deleteposition.top_left ... bottom_right). A new table replaces the one already at its position, so building one on every bar does not pile them up.width and height are a percent of the pane instead.table.cell again changes only the arguments you pass.tooltip shows it.Ten examples ship with the editor. Open one from Open ▾ → Examples, or from the Indicators list under My scripts. Each one is run by an automated test, so a change to the language cannot quietly break it.
| Example | What it shows |
|---|---|
| EMA Cross | Two EMAs with buy and sell markers where they cross. |
| RSI with Bands | RSI in its own pane with overbought and oversold levels. |
| Bollinger Bands | Bands around a moving average, with the space between shaded. |
| MACD Histogram | MACD with a histogram that is brighter while momentum grows. |
| ATR Channel | A moving average with bands a multiple of ATR away. |
| Daily Open | A line at each day's opening price, restarting every day. |
| Higher-Timeframe EMA | An EMA from a higher timeframe, shown only once that timeframe's bar has closed. |
| Swing Pivots | Labels each swing high and low once enough bars have passed to confirm it. |
| Heikin Ashi Candles | Smoothed Heikin Ashi candles in their own pane, built with plotcandle. |
| Inside Bar | Highlights bars that trade entirely inside the previous bar. |
//@version=5
indicator("Bollinger Bands", overlay = true)
len = input.int(20, "Length", minval = 1)
mult = input.float(2.0, "Multiplier", minval = 0.1, step = 0.1)
[basis, upper, lower] = ta.bb(close, len, mult)
plot(basis, "Basis", color.orange)
u = plot(upper, "Upper", color.blue)
l = plot(lower, "Lower", color.blue)
fill(u, l, color.new(color.blue, 90))
//@version=5
indicator("Daily Open", overlay = true)
newDay = dayofmonth != dayofmonth[1]
var float dailyOpen = na
if newDay
dailyOpen := open
plot(dailyOpen, "Daily open", color.yellow, 2, plot.style_linebr)
//@version=5
indicator("RSI with Bands")
len = input.int(14, "Length", minval = 1)
upper = input.int(70, "Overbought")
lower = input.int(30, "Oversold")
value = ta.rsi(close, len)
plot(value, "RSI", color.purple, 2)
hline(upper, "Overbought", color.red, hline.style_dashed)
hline(lower, "Oversold", color.green, hline.style_dashed)
bgcolor(value > upper ? color.new(color.red, 85) : value < lower ? color.new(color.green, 85) : na)
Some Pine features are refused on purpose, and some are not built yet. In both cases the editor names the line and the reason.
| Feature | State |
|---|---|
strategy() | Refused. This app is for manual trading, and a script that places orders would bypass the fill rules. Write an indicator(). |
lookahead = barmerge.lookahead_on | Refused. It would hand a bar its higher-timeframe values before that bar has closed. |
math.random() | Refused. A script must give the same output every time. |
Other symbols, request.security_lower_tf, other request.* calls | Not available. A script is given the chart's own candles only. request.security works for the chart's own symbol on an equal or higher timeframe. |
request.security on weekly or monthly | Not offered. The app's bars stop at one day. |
alert(), alertcondition() | Accepted and ignored. |
max_*_count.max_polylines_count.The indicators on your chart, and the scripts you save, are kept when you close the app.
Every indicator on the chart, built-in and script, with its settings, is saved a moment after each change and comes back when the app starts. This includes whether it is collapsed, hidden or locked.
active_indicators.json in the app data folder (on Windows, %APPDATA%\com.onplayfx.barreplay\). It is written to a temporary file first and then renamed, so a crash cannot leave half a file.Save (Ctrl S) puts the script in your library: <app data>/scripts/<id>.json. It then shows under Open ▾ → Saved and in the Indicators list under My scripts. The × beside a saved script deletes it.
A library is an ordinary saved script that begins with library("Name"). Scripts that import it find it among your saved scripts, at startup and again whenever you save or delete one.
A script on the chart carries its own source. Chart templates, undocked chart windows and the indicator list all work without looking anything up, and editing the saved copy later does not silently change a script already on the chart.
Which scripts are on a chart is not saved inside a replay session (nor are the built-in indicators). To keep a particular set, save a chart template.
Errors show the line number and what went wrong. If there is no error and the picture is not what you expected, check this list.
| What you see | Why, and what to do |
|---|---|
| A line starts part-way along the chart | A lookback such as ta.sma(close, 200) has no value until 200 bars exist. The early bars are na. |
| Two equal prices are not equal | Prices are decimals computed from integer points. Compare with a tolerance: math.abs(a - b) < syminfo.mintick / 2. |
| Hundreds of labels or lines, then old ones vanish | line.new inside a plain statement makes a new one every bar, and the oldest is deleted at the limit. Declare it with var, or delete the old one first. |
| An array element read gives the wrong thing | a[i] is the history operator, not element access. Use a.get(i). |
| “index out of range” or an empty array error | The index is outside the array, or you called pop, shift, first or last on an empty one. Check size() first. |
| “Cannot read 'x': the value is na” | The object was never created (var p = na). Create it with Name.new(...) before you read a field. |
| “There is no method ... for that value” | The method is defined for another type, or the value is not what you think. Check the first parameter's type. |
| “There is no library called ...” | The library is not saved, or its library("Name") title differs from the name in the import. Save the library and check the spelling. |
| “In library 'X', line N: ...” | The error is inside the library, not in the script that imports it. Open the library, fix that line, and save. |
| Add to chart says the script is a library | Libraries are imported, not added. Save it and write import in an indicator. |
| A higher-timeframe line is stepped | That is correct. It changes only when a higher-timeframe bar closes. See Other timeframes. |
A request.security error about a variable | The expression uses a variable changed with := or declared with var, which cannot be recomputed on the higher timeframe. Put the calculation inside the expression. |
A ta.* inside if reads too little history | Its argument changes from bar to bar, so skipped bars cannot be replayed. Call it outside the if and use the result inside. |
| A drawing is not visible | Labels, lines and boxes do not affect the price scale. One far outside the visible prices is off screen. |
| Stopped after 500,000 iterations | A loop on one bar never ended or is too large. Check its exit condition. |
| A value differs from TradingView | See Where it differs from TradingView: ta.ema seeding, pivots reported late, and ta.kc using an EMA of the true range. |
A list of the script editor's features and how each one has been checked, so you know what to trust and what to test.
IN THE APP used in the desktop app and working.
ON SCREEN drawn in a browser test page and looked at, not yet in the app window.
TESTED covered by automated tests (838 pass), not yet looked at on a chart.
| Area | State | Notes |
|---|---|---|
| Editor, run, update on chart, save to library | IN THE APP | Autocomplete (Ctrl Space), error lines, examples. |
| plot, hline, fill, bgcolor, shapes | IN THE APP | All plot styles and per-bar colours. |
| Inputs and the settings gear | IN THE APP | Including the timeframe dropdown. |
Higher timeframes (request.security) | IN THE APP | Closed bars only, no repainting. |
| Labels, lines, boxes | ON SCREEN | Extend, styles, bar-time positions. |
| Tables, merged cells | ON SCREEN | All eight positions, alignment, percent sizes. |
| Hover tooltips on labels and cells | ON SCREEN | |
| Arrays, maps, matrices | TESTED | Output goes through plots and labels, which are covered above. |
| Types and methods | TESTED | Stepping a session equals a full run. |
Enums, input.enum | TESTED | The dropdown shows titles; the saved value is the name. |
Libraries and import | TESTED | Including nested imports, private helpers and charts re-running after a save. |
polyline, chart.point | TESTED | Geometry is tested; not yet seen in the app window. |
ta.* skipped on some bars | TESTED | Matches the unconditional value. |
barcolor | TESTED | Not yet seen in the app window. |
plotcandle, plotbar, plotarrow | TESTED | Not yet seen in the app window. |
linefill | TESTED | Not yet seen in the app window. |
| Indicators kept across restarts | TESTED | Store and file tests pass. Restart the app once to confirm it on your machine. |
request.security_lower_tf and other symbols. Both need data the script engine is not given (finer bars, or a second symbol aligned to the replay), so they stop with a message that says so. strategy() is refused on purpose. See What it will not do.
If a script behaves oddly, copy it and its error line. It becomes a test, and the fix keeps working.
Changes to the script editor, newest first.
Enums with an input.enum dropdown, polylines built from chart.point values, and libraries: save a script that begins with library("Name") and import its exported functions, types and enums from any indicator. p[1] on an object variable is covered by a test.
The indicators on your chart, built-in and script, come back when the app starts, with their settings. See Saving and restoring.
table.* now draws real tables: merged cells, alignment, borders, percent widths and heights. Labels and table cells show their tooltip when you hover them.
The full set of map functions and matrix maths (mult, det, inv, transpose and more). Generic syntax such as map.new<string, float>() is accepted. Define your own type and method, and call x.method() on collections, drawings, objects and even float and string.
ta.highest(high, 30) inside if barstate.islast used to return na. It now sees every bar, so channel-drawing scripts draw. linefill, the chart.* colours and flags, weekofyear and extra syminfo.* values were added at the same time.
More ta.* functions, volume-based series, time_close, and many str.* helpers.
plotcandle, plotbar, plotarrow and barcolor, with Heikin Ashi as an example.
label.*, line.* and box.* with styles, extends, bar-time positions and drawing limits.
The array library, in function and method form, with for item in a loops.
request.security for the chart's own symbol, closed bars only, plus input.timeframe and input.session.
The editor with highlighting and autocomplete, indicators-only scripts, the settings gear, the script library, and the examples.