Type/to search
8
Follow
1364
Followers
Analisar o índice de Sharpe, o drawdown máximo, a taxa de retorno e outros algoritmos indicadores em backtesting de estratégia
Discussions
Created 2022-11-26 15:13:17  Updated 2023-09-18 20:21:39
 0
 2904

img

Analisar o índice de Sharpe, o drawdown máximo, a taxa de retorno e outros algoritmos indicadores em backtesting de estratégia

Os membros do grupo frequentemente discutem alguns algoritmos de indicadores de desempenho de estratégias, e um algoritmo também é divulgado na documentação da API do FMZ. No entanto, é um pouco difícil de entender sem comentários. Neste artigo, vou levá-lo a analisar este algoritmo. Acredito que depois de ler este artigo, você deve ter uma melhor compreensão dos conceitos e lógica de cálculo da razão de Sharpe, máximo drawdown e taxa de retorno. Todos têm um entendimento mais claro.

Vamos diretamente ao código-fonte, que é escrito em JavaScript. O sistema de backtesting da FMZ também usa esse algoritmo para gerar automaticamente dados de desempenho de backtesting.

função returnAnalyze

function returnAnalyze(totalAssets, profits, ts, te, period, yearDays)

https://www.fmz.com/api#回测系统夏普算法

Como é uma função computacional, ela deve ter entrada e saída. Vamos primeiro olhar para a entrada da função:

totalAssets, profits, ts, te, period, yearDays
  • totalAssets
    Este parâmetro é o total de ativos iniciais quando a estratégia começa a ser executada.

  • profits
    Este parâmetro é relativamente importante porque uma série de cálculos de indicadores de desempenho são baseados nesses dados brutos. Este parâmetro é uma matriz bidimensional, o formato é o seguinte:[[timestamp1, profit1], [timestamp2, profit2], [timestamp3, profit3], ....., [timestampN, profitN]], podemos ver que a função returnAnalyze requer uma estrutura de dados que registre a ordem cronológica dos retornos em cada momento. timestamp1 a timestampN estão em ordem de longe para perto no tempo. Há um valor de lucro em cada momento. Assim, por exemplo, o terceiro ponto de tempo no registro de receita é[timestamp3, lucro3]. No sistema de backtesting na linha FMZ, os dados da matriz de lucros são fornecidos a esta função pelo sistema de backtesting. Claro, se você registrar os dados de lucro para formar tal estrutura de matriz, você também pode fornecê-los a esta função de cálculo para calcular o resultado.

  • ts
    O registro de data e hora de início do backtest.

  • te
    O registro de data e hora final do backtest.

  • period
    Ciclos computacionais em milissegundos.

  • yearDays
    Dias de negociação em um ano.

A seguir, vamos dar uma olhada na saída desta função:

return { totalAssets: totalAssets, yearDays: yearDays, totalReturns: totalReturns, annualizedReturns: annualizedReturns, sharpeRatio: sharpeRatio, volatility: volatility, maxDrawdown: maxDrawdown, maxDrawdownTime: maxDrawdownTime, maxAssetsTime: maxAssetsTime, maxDrawdownStartTime: maxDrawdownStartTime, winningRate: winningRate }
  • totalAssets: Patrimônio líquido inicial
  • yearDays: dias de negociação
  • totalReturns: taxa de retorno cumulativa
  • annualizedReturns: retorno anualizado
  • sharpeRatio: Razão de Sharpe
  • Volatilidade: volatilidade
  • maxDrawdown: Retração máxima
  • maxDrawdownTime: Carimbo de data e hora do rebaixamento máximo
  • maxAssetsTime: registro de data e hora do valor líquido máximo
  • maxDrawdownStartTime: Tempo máximo de início da retração
  • winningRate: taxa de vitória

img

Conhecendo a entrada e a saída, agora entendemos para que serve essa função. Simplificando, forneça a esta função alguns registros brutos, como uma matriz de estatísticas de receita. A função calculará um resultado para você mostrar o desempenho do backtest.

A seguir, vamos ver como o código é calculado:

javascript
function returnAnalyze(totalAssets, profits, ts, te, period, yearDays) { // force by days period = 86400000 // 一天的毫秒数,即 60 * 60 * 24 * 1000 if (profits.length == 0) { // 如果参数profits数组长度为0,无法计算直接返回空值 return null } var freeProfit = 0.03 // 无风险利率 ,也可以根据需求设置,例如国债年化3% var yearRange = yearDays * 86400000 // 一年所有累计的交易日的毫秒数 var totalReturns = profits[profits.length - 1][1] / totalAssets // 累计收益率 var annualizedReturns = (totalReturns * yearRange) / (te - ts) // 年华收益率,把收益统计的时间缩放到一年的尺度上得出的预期收益率 // MaxDrawDown var maxDrawdown = 0 // 初始化最大回撤变量为0 var maxAssets = totalAssets // 以初始净值赋值初始化最大资产变量 var maxAssetsTime = 0 // 初始化最大资产时刻的时间戳 var maxDrawdownTime = 0 // 初始化最大回撤时刻的时间戳 var maxDrawdownStartTime = 0 // 初始化最大回撤开始时刻的时间戳 var winningRate = 0 // 初始化胜率为0 var winningResult = 0 // 记录赢的次数 for (var i = 0; i < profits.length; i++) { // 遍历收益数组 if (i == 0) { if (profits[i][1] > 0) { // 如果第一个收益记录点,收益大于0,表示盈利 winningResult++ // 赢的次数累加1 } } else { // 如果不是第一个收益记录点,只要当前的点的收益,大于前一个时刻(收益点)的收益,表示盈利,赢的次数累加1 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) { // 当记录的最大资产数值大于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 // 把rangeEnd处理为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++ // 累加i用于访问下一个profits节点 } perRatio = ((dayProfit / totalAssets) * yearRange) / period // 计算当时年华的收益率 sum += perRatio // 累计 datas.push(perRatio) // 放入数组 datas } var sharpeRatio = 0 // 初始夏普比率为0 var volatility = 0 // 初始波动率为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); // std用于计算后面的方差,后面的std / datas.length就是方差,求算数平方根就是标准差 } 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 } }

O algoritmo não é complicado no geral, mas pode haver alguns conceitos que precisam ser entendidos com antecedência.

  • variância:
    Pode ser entendido como um conjunto de dados de receita:
    A média do conjunto amostral 1, 2, 3, 4, 5 é (1+2+3+4+5)/5=3, e a variância é a soma dos quadrados das diferenças entre cada ponto de dados e seus média. A média de é:[(1-3)^2+(2-3)^2+(3-3)^2+(4-3)^2+(5-3)^2]/5=2, e a variância é 2 .

  • Desvio Padrão:
    Calcule a raiz quadrada da variância, que é o desvio padrão.

  • Volatilidade:
    Quando a escala de cálculo é anualizada, a volatilidade é o desvio padrão.

Depois de entender esses conceitos e fórmulas de cálculo, a parte do cálculo de Sharpe da função ficará clara imediatamente.
Fórmula de cálculo do índice de Sharpe: (retorno anualizado - taxa livre de risco) / desvio padrão

Você aprendeu?

Comment
All comments (0)
No data
No data
  • 1
iPhone Download
Forums
PINE Language
© 2015 - ∞ INVENTOR PTE LTD (SG)