HorizonAI App

Backtesting in HorizonAI

Run a Python strategy against real market data without leaving the chat

Python mode has its own backtester, and it is the quickest one in HorizonAI. It runs synchronously against candles we already have — there's no worker queue and no terminal to boot, so a result comes back in a couple of seconds. Better still, the assistant can run it itself in the middle of a reply and read the numbers straight back into the conversation.

This guide covers the Python backtester. For MQL5 see Backtesting MT5 Strategies in HorizonAI; for Pine Script see Backtesting Pine Script Strategies in HorizonAI.

What it is

A vectorized backtest engine built into the Python sandbox. Your script declares entries and exits with set_long_entry, set_long_exit, set_short_entry and set_short_exit; the engine walks the candles, applies your stops and targets, and returns a full report — profitability, risk, trade analysis, an equity curve, a drawdown curve, monthly returns, a P&L distribution and every closed trade.

The differentiator versus the MT5 backtester isn't just speed. MT5 queues a job on a real terminal and you wait. Python runs in-process, and because it's in-process the assistant has a tool for it — so "backtest this and tell me what's wrong with it" is a single turn, not three.

1) Write a strategy, not an indicator

An indicator has nothing to simulate. Your script has to declare at least one entry:

  1. Open a Python chat at app.horizontrading.ai.
  2. Ask for a strategy, e.g. "Go long when the 20 EMA crosses above the 50, exit on the reverse cross, 2% stop and 3% target."
  3. The assistant writes the entries and exits alongside the chart drawing, so the same script both plots and backtests.

If the run comes back with "This script has no entries, so there is nothing to backtest", you have an indicator. Ask the assistant to "turn this into a strategy with entries and exits."

2) Open the Backtest tab

In the right-side panel, switch from Code to Backtest. The controls row holds three sub-tab buttons (Overview, Report, Trades), the symbol picker, the timeframe picker and the Run button.

Symbol and timeframe are the same pair the Chart tab uses — change them in either place and both follow.

3) Pick a symbol and timeframe

The symbol box opens on a curated list and searches a much wider universe as you type:

  • Forex — EUR/USD, GBP/USD, USD/JPY, USD/CHF, AUD/USD, USD/CAD
  • Commodities — XAU/USD (Gold), XAG/USD (Silver)
  • Crypto — BTC/USD, ETH/USD, SOL/USD, DOGE/USD, AVAX/USD, LINK/USD
  • Stocks — AAPL, MSFT, GOOGL, AMZN, TSLA, NVDA, META

Timeframes: 1m, 5m, 15m, 30m, 1h, 4h, 1D.

There are no date pickers. The window is fixed at roughly the most recent 730 bars on intraday timeframes and 1,825 bars on 1D — the Report tells you exactly how many bars were tested.

4) Click Run

Hit Run. Most runs finish in a few seconds. Symbol and timeframe lock while a run is in flight so an incoming result can't get labelled with the wrong instrument.

Let the assistant run it for you. Say "backtest this" and it will pick a symbol and timeframe, run it itself, and quote the return, win rate, profit factor, drawdown, Sharpe and trade count back to you in the same reply — then offer a change. It defaults to EUR/USD on 1D if you don't say otherwise.

5) Read the results

Three sub-tabs:

  • Overview — the read-at-a-glance view. Net profit and the balance it came from, return, profit factor, expectancy, win rate, Sharpe/Sortino/Calmar, max drawdown, trade count and bars tested, a long-vs-short split, how trades ended, and the equity curve.
  • Report — the formal, exhaustive version: gross profit and loss, profit factor, expectancy, recovery factor, max drawdown, average and largest win/loss, win/loss ratio, winning and losing counts, max consecutive wins and losses, trade durations, exposure, long vs short P&L, starting capital and final equity — plus monthly returns, the P&L distribution, an exit-reason breakdown and a drawdown-over-time chart.
  • Trades — the price chart with your entry and exit markers drawn on it, above a sortable table of every closed trade: direction, entry and exit time and price, duration, P&L in dollars and percent, and why it closed (stop, target, signal, or end of data).

Exports: the Report header has a JSON button for the whole result; the Trades tab has a CSV button for the trade list.

6) Iterate

The assistant can see the metrics, the trade list and your code at the same time, so ask it directly: "why is the drawdown so deep?", "try a wider stop", "does it still work on gold?" It will edit the strategy and offer to run it again.

Limits, defaults & fine print

  • Who can run it: running a backtest requires Pro or Elite. On other plans the Run button shows an upgrade panel instead of a result — writing and charting Python stays free.
  • Starting capital: $10,000, fixed.
  • Commission: 0.1% per trade, charged on entry and on exit.
  • Slippage: none modelled.
  • Execution: bar close. One position at a time — a new entry only fires when flat.
  • Position size: a fraction of current equity, 100% by default; stops and targets take a percentage or a series of absolute prices.
  • Any open position is closed at the last bar and appears in the trade list with an "end of data" reason.
  • Timeout: 30 seconds per run.
  • Sharpe, Sortino and Calmar are annualised on a 252-period assumption regardless of the timeframe you ran, so treat them as comparative, not absolute.
  • No queue and nothing persisted per run — results are cached briefly and re-shown, not archived as a job history.

Past performance does not guarantee future results. This backtester models a 0.1% commission and nothing else — no spread, no slippage, no market impact, no liquidity, no overnight financing. Treat the report as a sanity check on your logic, not a forecast.

When to use this vs. the MT5 backtester

Use the Python backtester when…Use the MT5 backtester when…
You're still deciding whether the idea is worth anythingYou want the numbers a real MetaTrader 5 terminal produces
You want the assistant to run it and iterate in one turnYou want broker-realistic spread on real MT5 tick history
You want an answer in seconds, not a queue positionYou're testing an Expert Advisor you'll actually deploy to MT5

Most people do both: prove the edge in Python, then export to MQL5 and re-test it on MT5 before going anywhere near live.

Troubleshooting

  • "This script has no entries, so there is nothing to backtest" — the script only draws. Ask for set_long_entry / set_short_entry to be added.
  • Zero trades, everything reads $0 — the entry condition never became true in the window. Loosen it, try a more volatile symbol (XAU/USD is a good stress test), or move to a lower timeframe for more bars.
  • "Backtest timeout (max 30 seconds)" — usually a loop over bars. Ask the assistant to "rewrite this vectorized."
  • The Run button shows an upgrade panel — backtesting is on Pro and Elite. The Chart tab still works on your plan.
  • Profit factor reads ∞ — there were winning trades and no losing ones. It's a sample-size problem, not an edge.
  • "Couldn't load market data" — that listing has no history available. Pick another symbol from the curated list.

Understanding Your Results