
Các thành viên trong nhóm thường thảo luận về một số thuật toán chỉ số hiệu suất của các chiến lược và một thuật toán cũng được tiết lộ trong tài liệu API FMZ. Tuy nhiên, hơi khó hiểu nếu không có bình luận. Trong bài viết này, tôi sẽ đưa bạn đi phân tích thuật toán này. Tôi tin rằng sau khi đọc bài viết này, bạn sẽ hiểu rõ hơn về các khái niệm và logic tính toán của tỷ lệ Sharpe, tối đa rút vốn và tỷ lệ lợi nhuận. Mọi người đều hiểu rõ hơn.
Chúng ta sẽ đi thẳng đến mã nguồn được viết bằng JavaScript. Hệ thống kiểm tra ngược của FMZ cũng sử dụng thuật toán này để tự động tạo dữ liệu hiệu suất kiểm tra ngược.
function returnAnalyze(totalAssets, profits, ts, te, period, yearDays)
https://www.fmz.com/api#%E5%9B%9E%E6%B5%8B%E7%B3%BB%E7%BB%9F%E5%A4%8F%E6%99%AE%E7%AE%97%E6%B3%95
Vì đây là một hàm tính toán nên nó phải có đầu vào và đầu ra. Trước tiên chúng ta hãy xem xét đầu vào của hàm:
totalAssets, profits, ts, te, period, yearDays
totalAssets Tham số này là tổng tài sản ban đầu khi chiến lược bắt đầu chạy.
profits
Tham số này là một tham số tương đối quan trọng vì một loạt các tính toán chỉ số hiệu suất đều dựa trên dữ liệu thô này. Tham số này là một mảng hai chiều, định dạng như sau:[[timestamp1, profit1], [timestamp2, profit2], [timestamp3, profit3], ....., [timestampN, profitN]], chúng ta có thể thấy rằng hàm returnAnalyze yêu cầu một cấu trúc dữ liệu ghi lại thứ tự thời gian trả về tại mỗi thời điểm. timestamp1 đến timestampN được sắp xếp theo thứ tự thời gian từ xa đến gần. Tại mỗi thời điểm đều có giá trị lợi nhuận. Vì vậy, ví dụ, thời điểm thứ ba trong hồ sơ doanh thu là[dấu thời gian3, lợi nhuận3]. Trong hệ thống kiểm tra ngược trên dòng FMZ, dữ liệu 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 lợi nhuận để tạo thành cấu trúc mảng như vậy, bạn cũng có thể cung cấp dữ liệu đó cho hàm tính toán này để tính toán kết quả.
ts Dấu thời gian bắt đầu của thử nghiệm ngược.
te Dấu thời gian kết thúc của quá trình kiểm tra ngược.
period Chu kỳ tính toán tính bằng mili giây.
yearDays 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:
return {
totalAssets: totalAssets,
yearDays: yearDays,
totalReturns: totalReturns,
annualizedReturns: annualizedReturns,
sharpeRatio: sharpeRatio,
volatility: volatility,
maxDrawdown: maxDrawdown,
maxDrawdownTime: maxDrawdownTime,
maxAssetsTime: maxAssetsTime,
maxDrawdownStartTime: maxDrawdownStartTime,
winningRate: winningRate
}

Khi biết được đầu vào và đầu ra, chúng ta đã hiểu hàm này được sử dụng để làm gì. Nói một cách đơn giản, hãy cung cấp cho hàm này một số bản ghi thô, chẳng hạn như một mảng thống kê doanh thu. Hàm này sẽ tính toán kết quả để bạn thấy được hiệu suất kiểm thử ngược.
Tiếp theo, chúng ta hãy xem cách tính toán mã:
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
}
}
Nhìn chung thuật toán không phức tạp, nhưng có thể có một số khái niệm cần phải hiểu trước.
phương sai: Có thể hiểu là một tập hợp dữ liệu doanh thu: Giá trị trung bình của tập mẫu 1, 2, 3, 4, 5 là (1+2+3+4+5)/5=3, và phương sai là tổng bình phương của các điểm khác biệt giữa mỗi điểm dữ liệu và điểm của nó. trung bình. Trung bình của là:[(1-3)^2+(2-3)^2+(3-3)^2+(4-3)^2+(5-3)^2]/5=2, và phương sai là 2 .
Độ lệch chuẩn: Lấy căn bậc hai của phương sai, tức là độ lệch chuẩn.
Độ biến động: Khi thang tính toán được tính theo năm, độ biến động chính là độ lệch chuẩn.
Khi bạn đã hiểu những 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ẽ trở nên rõ ràng ngay từ cái nhìn đầu tiên. Công thức tính tỷ lệ Sharpe: (lợi nhuận hàng năm - tỷ lệ không rủi ro) / độ lệch chuẩn
Bạn đã học được điều đó chưa?