输入/搜索内容
入门
欢迎使用发明者量化交易平台
快速开始
密钥安全性
平台基础
账号与计费
实盘计费与充值
子账号
交易所
通用协议
本地凭据文件
交易所特殊说明
证券与期货
加密货币
托管者
策略库
实盘
编写策略
开发工具
回测系统
进阶专题
数据与研究
对外接口

回测结束后,回测页面显示收益曲线、统计指标、状态信息、日志信息和账户信息。

收益曲线

收益曲线由策略调用LogProfit()记录的收益值组成(LogProfit)。策略不调用LogProfit()时没有收益曲线,下面依赖收益序列的统计指标也无法计算,只能从账户信息中查看回测结束时的资产。MCP工具get_backtest返回的profit、max_drawdown同样来自LogProfit()。

统计指标

统计指标由收益序列profits(每个元素为[时间戳, 收益])和初始资产totalAssets按下面的算法计算:

指标含义
收益率(totalReturns)最后一个收益值 ÷ 初始资产。
年化收益(annualizedReturns)收益率 × 一年的时长(yearDays天)÷ 回测时长,按比例线性折算。
最大回撤(maxDrawdown)资产(初始资产 + 收益)相对此前最高点下跌的最大比例。maxDrawdownStartTime为该最高点的时间,maxDrawdownTime为回撤最深的时间。
胜率(winningRate)收益序列中高于前一个点的点所占的比例(第一个点与0比较)。统计的是收益记录,不是逐笔交易的胜率。
波动率(volatility)把回测时间按天切分,每天的收益额 ÷ 初始资产,再乘以yearDays折算为年化值(没有收益记录的日子按0计),取这些值的总体标准差。
夏普比率(sharpeRatio)(年化收益 − 无风险利率3%)÷ 波动率;波动率为0时为0。

yearDays是年化时使用的一年天数,由回测页面传入。注意波动率是把每日收益率直接乘以yearDays年化,而不是常见的乘以√yearDays,因此这里的夏普比率不宜与其他平台的数值直接比较。

算法源码:

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 } }

数据下载

  • 状态栏数据下载:回测结束后,在「状态信息」栏右上角点击「下载表格」,下载回测结束时状态栏数据的CSV文件。
  • 日志数据下载:在「日志信息」栏右上角点击「下载表格」,下载回测日志的CSV文件。