Leve-o para analisar a taxa Sharpe, retirada máxima, taxa de retorno e outros algoritmos indicadores na estratégia backtesting

Autora:Lydia., Criado: 2022-11-28 16:44:25, Atualizado: 2023-09-13 19:42:38

img

Leve-o para analisar a taxa Sharpe, retirada máxima, taxa de retorno e outros algoritmos indicadores na estratégia backtesting

Alguns algoritmos de indicador de desempenho da estratégia são frequentemente discutidos pelos membros do grupo, e um algoritmo também foi divulgado no documento API da FMZ. No entanto, não é fácil de entender sem comentários. Neste artigo, vou levá-lo a analisar o algoritmo. Acredito que você terá uma compreensão clara dos conceitos de Sharpe ratio, drawdown máximo, taxa de retorno e a lógica de cálculo após ler este artigo. O sistema de backtesting do FMZ também adota este algoritmo para gerar dados de desempenho de backtesting automaticamente.

Função returnAnalise

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

https://www.fmz.com/api#BacktestingSistema Algoritmo Sharpe

Como é uma função de cálculo, deve haver entradas e saídas.

totalAssets, profits, ts, te, period, yearDays
  • Total dos activos Este parâmetro é o total de activos iniciais quando a estratégia começa a funcionar.

  • lucros Este parâmetro é importante, porque uma série de indicadores de desempenho são calculados com base nestes dados originais.[[timestamp1, profit1], [timestamp2, profit2], [timestamp3, profit3],..., [timestampN, profitN]]. Pode-se ver que a função returnAnalyze precisa de uma estrutura de dados que registre a ordem cronológica de retornos a cada momento. O timestamp1 para o timestampN estão em ordem cronológica de longe para perto. Há um valor de lucro em cada ponto de tempo. Por exemplo, o terceiro ponto de tempo no registro de retornos é [timestamp 3, profit3]. No sistema de backtesting on-line do FMZ, os dados da matriz de lucros são fornecidos a esta função pelo sistema de backtesting.

  • Tss A data de início do backtest.

  • Te O carimbo do final do backtest.

período Período de cálculo ao nível dos milissegundos.

  • anoDias Os dias de troca num ano.

Em seguida, vamos olhar para a saída desta função juntos:

return {
        totalAssets: totalAssets,
        yearDays: yearDays,
        totalReturns: totalReturns,
        annualizedReturns: annualizedReturns,
        sharpeRatio: sharpeRatio,
        volatility: volatility,
        maxDrawdown: maxDrawdown,
        maxDrawdownTime: maxDrawdownTime,
        maxAssetsTime: maxAssetsTime,
        maxDrawdownStartTime: maxDrawdownStartTime,
        winningRate: winningRate
    }
  • Total dos activos: total dos activos
  • anoDias de negociação
  • Total de rendimentos: rendimentos totais
  • Retorno anualizado: retorno anualizado
  • SharpeRatio: Relação Sharpe
  • volatilidade: volatilidade
  • Maximum drawdown: máximo de retirada
  • maxDrawdownTime: carimbo de tempo no máximo de extracção
  • maxAssetsTime: carimbo de tempo no ativo máximo
  • maxDrawdownStartTime: hora de início da retirada máxima
  • Rate de vitória: taxa de vitória

Conhecendo a entrada e saída, podemos entender para que a função é usada. Para simplificar, ela dá à função alguns registros originais, como a matriz de estatísticas de retorno. A função dará um resultado para mostrar o desempenho do backtest.

Em seguida, vamos ver como o código é calculado:

function returnAnalyze(totalAssets, profits, ts, te, period, yearDays) {
    // force by days
    period = 86400000                  // The number of milliseconds in a day, that is 60 * 60 * 24 * 1000
    if (profits.length == 0) {         // If the length of the array of profits is 0, it cannot be calculated and it will return to the null value directly
        return null
    }
    var freeProfit = 0.03              // Risk-free interest rate, which can also be set according to the demand, such as 3% annualized national debt
    var yearRange = yearDays * 86400000          // Milliseconds of all accumulated trading days in a year
    var totalReturns = profits[profits.length - 1][1] / totalAssets      // Total return rate
    var annualizedReturns = (totalReturns * yearRange) / (te - ts)       // The annualized rate of return, the expected rate of return obtained by scaling the time of profit statistics to the scale of one year

    // MaxDrawDown
    var maxDrawdown = 0           // Initialize the maximum drawdown variable to 0
    var maxAssets = totalAssets   // Initialize maximum asset variable with initial net value assignment
    var maxAssetsTime = 0         // Timestamp of the time when the maximum asset is initialized
    var maxDrawdownTime = 0       // Timestamp of the time when the maximum drawdown is initialized
    var maxDrawdownStartTime = 0  // Timestamp of the time when the maximum start time is initialized
    var winningRate = 0           // Initialized win rate is 0
    var winningResult = 0         // Record the number of win time
    for (var i = 0; i < profits.length; i++) {      // Traverse the return array
        if (i == 0) {
            if (profits[i][1] > 0) {                // If the first return record point, the return is greater than 0, it means the profit
                winningResult++                     // The number of win times accumulates 1 
            }
        } else {                                    // If it is not the first returns record point, as long as the returns of the current point is greater than the returns of the previous moment (return point), it means profit, and the number of win times accumulates 1 
            if (profits[i][1] > profits[i - 1][1]) {
                winningResult++
            }
        }
        if ((profits[i][1] + totalAssets) > maxAssets) {    // If the return plus the initial net value at that moment is greater than the largest asset recorded to have occurred, the value of the largest asset is updated and the timestamp of this moment is recorded
            maxAssets = profits[i][1] + totalAssets
            maxAssetsTime = profits[i][0]
        }
        if (maxAssets > 0) {                                // When the maximum asset value recorded is greater than 0, the drawdown is calculated
            var drawDown = 1 - (profits[i][1] + totalAssets) / maxAssets
            if (drawDown > maxDrawdown) {                   // If the current drawdown is greater than the recorded maximum drawdown, update the maximum drawdown, maximum drawdown time, etc
                maxDrawdown = drawDown
                maxDrawdownTime = profits[i][0]
                maxDrawdownStartTime = maxAssetsTime
            }
        }
    }
    if (profits.length > 0) {                            // Calculate the winning rate
        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     // Process rangeEnd as an integer multiple of 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) {    // Ensure that when the timestamp is not out of bounds, the array length is also not out of bounds
            dayProfit += (profits[i][1] - preProfit)           // Calculate the daily returns
            preProfit = profits[i][1]                          // Record yesterday's returns
            i++                                                // Accumulate i for accessing the next profits node
        }
        perRatio = ((dayProfit / totalAssets) * yearRange) / period   // Calculate the annualized rate of return at that time
        sum += perRatio                                               // Accumulation
        datas.push(perRatio)                                          // Put in the array datas
    }

    var sharpeRatio = 0                    // Initial Sharpe ratio is 0
    var volatility = 0                     // Initial volatility is 0
    if (datas.length > 0) {
        var avg = sum / datas.length;      // Find the mean value
        var std = 0;
        for (i = 0; i < datas.length; i++) {
            std += Math.pow(datas[i] - avg, 2);      // The std is used to calculate the following variance. The following std/datas.length is the variance, and the square root of the number is the standard deviation
        }
        volatility = Math.sqrt(std / datas.length);  // In terms of years, volatility is the standard deviation
        if (volatility !== 0) {
            sharpeRatio = (annualizedReturns - freeProfit) / volatility   // Sharpe formula to calculate Sharpe ratio: (annualized return rate - risk-free rate) / standard deviation
        }
    }

    return {
        totalAssets: totalAssets,
        yearDays: yearDays,
        totalReturns: totalReturns,
        annualizedReturns: annualizedReturns,
        sharpeRatio: sharpeRatio,
        volatility: volatility,
        maxDrawdown: maxDrawdown,
        maxDrawdownTime: maxDrawdownTime,
        maxAssetsTime: maxAssetsTime,
        maxDrawdownStartTime: maxDrawdownStartTime,
        winningRate: winningRate
    }
}

No geral, o algoritmo não é complexo e pode haver vários conceitos que precisam ser entendidos antecipadamente.

  • Variância: Pode ser entendido como um conjunto de dados de retorno. O grupo de amostras, 1, 2, 3, 4 e 5, tem um valor médio de (1+2+3+4+5)/5 = 3, enquanto a variância é o valor médio da soma dos quadrados das diferenças entre cada dado e sua soma, respectivamente, que é [(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 aritmética da variância, que é o desvio padrão.

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

Com a compreensão destes conceitos e fórmulas de cálculo, a parte de cálculo de Sharpe da função será clara de uma olhada. Fórmula de Sharpe para calcular o rácio de Sharpe: (taxa de retorno anualizada - taxa livre de risco) / desvio-padrão

Já aprendeste?


Mais.