Type/to search
Getting Started
Welcome to FMZ Quant Trading Platform
Quick Start
Key Security
Platform Basics
Account and Billing
Live Robot Billing and Top-up
Sub-accounts
Exchange
General Protocol
Local Credential Files
Exchange-Specific Notes
Securities and Futures
Crypto
Docker
Strategy Library
Live Trading
Writing Strategies
Development Tools
Backtesting System
Advanced Topics
Data and Research
Integrations

When a backtest finishes, the backtest page shows the profit curve, statistics, status information, logs and account information.

Profit curve

The profit curve consists of the values the strategy records with LogProfit() (LogProfit). If the strategy never calls LogProfit(), there is no profit curve and the statistics below, which depend on that series, cannot be computed; only the final assets in the account information remain. The profit and max_drawdown returned by the MCP tool get_backtest also come from LogProfit().

Statistics

The statistics are computed from the profit series profits (each element [timestamp, profit]) and the initial assets totalAssets with the algorithm below:

MetricMeaning
Return (totalReturns)Last profit value ÷ initial assets.
Annualized return (annualizedReturns)Return × one year (yearDays days) ÷ backtest duration, scaled linearly.
Max drawdown (maxDrawdown)The largest fractional drop of assets (initial assets + profit) from their previous peak. maxDrawdownStartTime is the time of that peak, maxDrawdownTime the time of the deepest point.
Win rate (winningRate)Share of points in the profit series that are higher than the previous point (the first point is compared with 0). It counts profit records, not individual trades.
Volatility (volatility)The backtest period is cut into days; each day's profit ÷ initial assets is multiplied by yearDays to annualize it (days without profit records count as 0); volatility is the population standard deviation of these values.
Sharpe ratio (sharpeRatio)(Annualized return − risk-free rate 3%) ÷ volatility; 0 when volatility is 0.

yearDays is the number of days per year used for annualization, passed in by the backtest page. Note that daily returns are annualized by multiplying by yearDays rather than the usual √yearDays, so this Sharpe ratio should not be compared directly with values from other platforms.

Algorithm source:

javascript
function returnAnalyze(totalAssets, profits, ts, te, period, yearDays) { // force by days period = 86400000 if (profits.length == 0) { return null } var freeProfit = 0.03 // 0.04 var yearRange = yearDays * 86400000 var totalReturns = profits[profits.length - 1][1] / totalAssets var annualizedReturns = (totalReturns * yearRange) / (te - ts) // MaxDrawDown var maxDrawdown = 0 var maxAssets = totalAssets var maxAssetsTime = 0 var maxDrawdownTime = 0 var maxDrawdownStartTime = 0 var winningRate = 0 var winningResult = 0 for (var i = 0; i < profits.length; i++) { if (i == 0) { if (profits[i][1] > 0) { winningResult++ } } else { if (profits[i][1] > profits[i - 1][1]) { winningResult++ } } if ((profits[i][1] + totalAssets) > maxAssets) { maxAssets = profits[i][1] + totalAssets maxAssetsTime = profits[i][0] } if (maxAssets > 0) { var drawDown = 1 - (profits[i][1] + totalAssets) / maxAssets if (drawDown > maxDrawdown) { maxDrawdown = drawDown maxDrawdownTime = profits[i][0] maxDrawdownStartTime = maxAssetsTime } } } if (profits.length > 0) { winningRate = winningResult / profits.length } // trim profits var i = 0 var datas = [] var sum = 0 var preProfit = 0 var perRatio = 0 var rangeEnd = te if ((te - ts) % period > 0) { rangeEnd = (parseInt(te / period) + 1) * period } for (var n = ts; n < rangeEnd; n += period) { var dayProfit = 0.0 var cut = n + period while (i < profits.length && profits[i][0] < cut) { dayProfit += (profits[i][1] - preProfit) preProfit = profits[i][1] i++ } perRatio = ((dayProfit / totalAssets) * yearRange) / period sum += perRatio datas.push(perRatio) } var sharpeRatio = 0 var volatility = 0 if (datas.length > 0) { var avg = sum / datas.length; var std = 0; for (i = 0; i < datas.length; i++) { std += Math.pow(datas[i] - avg, 2); } volatility = Math.sqrt(std / datas.length); if (volatility !== 0) { sharpeRatio = (annualizedReturns - freeProfit) / volatility } } return { totalAssets: totalAssets, yearDays: yearDays, totalReturns: totalReturns, annualizedReturns: annualizedReturns, sharpeRatio: sharpeRatio, volatility: volatility, maxDrawdown: maxDrawdown, maxDrawdownTime: maxDrawdownTime, maxAssetsTime: maxAssetsTime, maxDrawdownStartTime: maxDrawdownStartTime, winningRate: winningRate } }

Downloading data

  • Status bar data: after the backtest finishes, click "Download Table" at the top right of the "Status" panel to download the final status bar data as a CSV file.
  • Log data: click "Download Table" at the top right of the "Logs" panel to download the backtest logs as a CSV file.