Đưa bạn để phân tích tỷ lệ Sharpe, rút tiền tối đa, tỷ lệ lợi nhuận và các thuật toán chỉ số khác trong backtesting chiến lược

Tác giả:Lydia., Tạo: 2022-11-28 16:44:25, Cập nhật: 2023-09-13 19:42:38

img

Đưa bạn để phân tích tỷ lệ Sharpe, rút tiền tối đa, tỷ lệ lợi nhuận và các thuật toán chỉ số khác trong backtesting chiến lược

Một số thuật toán chỉ số hiệu suất của chiến lược thường được các thành viên nhóm thảo luận, và một thuật toán cũng đã được tiết lộ trong tài liệu API của FMZ. Tuy nhiên, nó không dễ hiểu mà không có bình luận. Trong bài viết này, tôi sẽ đưa bạn phân tích thuật toán. Tôi tin rằng bạn sẽ có sự hiểu biết rõ ràng về các khái niệm tỷ lệ Sharpe, thu hút tối đa, tỷ lệ lợi nhuận và logic tính toán sau khi đọc bài viết này. Chúng ta sẽ bắt đầu với mã nguồn, được viết bằng ngôn ngữ JavaScript. Hệ thống backtesting của FMZ cũng áp dụng thuật toán này để tạo dữ liệu hiệu suất backtesting tự động.

returnAnalyze chức năng

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

https://www.fmz.com/api#Backtestinghệ thống thuật toán Sharpe

Vì nó là một hàm tính toán, phải có đầu vào và đầu ra.

totalAssets, profits, ts, te, period, yearDays
  • Tổng tài sản Điều kiện này là tổng tài sản ban đầu khi chiến lược bắt đầu chạy.

  • lợi nhuận Thông số này rất quan trọng, bởi vì một loạt các chỉ số hiệu suất được tính trên cơ sở dữ liệu ban đầu này.[[timestamp1, profit1], [timestamp2, profit2], [timestamp3, profit3],..., [timestampN, profitN]]. Có thể thấy rằng hàm returnAnalyze cần một cấu trúc dữ liệu như vậy để ghi lại thứ tự thời gian của các lần trả về mỗi lần. Thời gian stamp1 đến thời gian stampN theo thứ tự thời gian từ xa đến gần. Có một giá trị lợi nhuận tại mỗi thời điểm. Ví dụ, thời điểm thứ ba trong hồ sơ trả về là [thời gian stamp 3, profit3]. Trong hệ thống kiểm tra ngược trực tuyến của FMZ, dữ liệu của mảng lợi nhuận được cung cấp cho hàm này bởi hệ thống kiểm tra ngược. Tất nhiên, nếu bạn tự ghi lại dữ liệu trả về và tạo ra cấu trúc mảng như vậy, bạn cũng có thể cung cấp nó cho hàm tính để tính toán kết quả.

  • ts Thời gian bắt đầu backtest.

  • t Thời gian kết thúc backtest.

thời gian Thời gian tính toán ở mức độ millisecond.

  • nămNgày Ngày giao dịch trong một năm.

Tiếp theo, chúng ta hãy xem kết quả của hàm này cùng nhau:

return {
        totalAssets: totalAssets,
        yearDays: yearDays,
        totalReturns: totalReturns,
        annualizedReturns: annualizedReturns,
        sharpeRatio: sharpeRatio,
        volatility: volatility,
        maxDrawdown: maxDrawdown,
        maxDrawdownTime: maxDrawdownTime,
        maxAssetsTime: maxAssetsTime,
        maxDrawdownStartTime: maxDrawdownStartTime,
        winningRate: winningRate
    }
  • Tổng tài sản: tổng tài sản
  • năm Ngày: ngày giao dịch
  • Tổng lợi nhuận: tổng lợi nhuận
  • Thu nhập hàng năm: thu nhập hàng năm
  • SharpeRatio: tỷ lệ Sharpe
  • biến động: biến động
  • MaxDrawdown: Maximum Drawdown (tối đa rút vốn)
  • maxDrawdownTime: dấu thời gian ở mức rút tối đa
  • maxAssetsTime: dấu thời gian tại tài sản tối đa
  • maxDrawdownStartTime: thời gian bắt đầu rút tiền tối đa
  • tỷ lệ thắng: tỷ lệ thắng

Biết được đầu vào và đầu ra, chúng ta có thể hiểu chức năng được sử dụng để làm gì. Nói đơn giản, nó cung cấp cho chức năng một số bản ghi ban đầu, chẳng hạn như mảng thống kê trả về.

Tiếp theo, hãy xem mã được tính như thế nào:

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

Nhìn chung, thuật toán không phức tạp và có thể có một số khái niệm cần phải được hiểu trước.

  • Sự khác biệt: Nó có thể được hiểu như một tập hợp dữ liệu trả về. Nhóm các mẫu, 1, 2, 3, 4 và 5, có giá trị trung bình là (1+2+3+4+5)/5 = 3, trong khi biến thể là giá trị trung bình của tổng phương của các khác biệt giữa mỗi dữ liệu và tổng của nó tương ứng, đó là [(1-3) ^ 2 + (2-3) ^ 2 + (3-3) ^ 2 + (4-3) ^ 2 + (5-3) ^ 2) / 5 = 2, và biến thể là 2.

  • Phạm lệch chuẩn: Tính toán gốc vuông số học của biến thể, đó là độ lệch chuẩn.

  • Khả năng biến động: Khi thang đo tính toán là hàng năm, biến động là độ lệch chuẩn.

Với sự hiểu biết về các khái niệm và công thức tính toán này, phần tính toán Sharpe của hàm sẽ rõ ràng ngay lập tức. Công thức Sharpe để tính tỷ lệ Sharpe: (tỷ lệ lợi nhuận hàng năm - tỷ lệ không rủi ro) / lệch chuẩn

Anh đã học nó chưa?


Thêm nữa