Algunos algoritmos de indicadores de rendimiento de la estrategia a menudo son discutidos por los miembros del grupo, y un algoritmo también se ha revelado en el documento API de FMZ. Sin embargo, no es fácil de entender sin comentarios. En este artículo, te llevaré a analizar el algoritmo. Creo que tendrás una comprensión clara de los conceptos de Sharpe ratio, extracción máxima, tasa de retorno y la lógica de cálculo después de leer este artículo. El sistema de backtesting de FMZ también adopta este algoritmo para generar datos de rendimiento de backtesting automáticamente.
function returnAnalyze(totalAssets, profits, ts, te, period, yearDays)
https://www.fmz.com/api#BacktestingSistema algoritmo de Sharpe
Ya que es una función de cálculo, debe haber entradas y salidas.
totalAssets, profits, ts, te, period, yearDays
Total de activos Este parámetro es el total de activos iniciales cuando la estrategia comienza a ejecutarse.
ganancias
Este parámetro es importante, ya que una serie de indicadores de rendimiento se calculan sobre la base de estos datos originales.[[timestamp1, profit1], [timestamp2, profit2], [timestamp3, profit3],..., [timestampN, profitN]]
. Se puede ver que la función returnAnalyze necesita una estructura de datos que registre el orden cronológico de las devoluciones en cada momento. Las marcas de tiempo 1 a N están en orden cronológico de lejos a cerca. Hay un valor de ganancia en cada punto de tiempo. Por ejemplo, el tercer punto de tiempo en el registro de devoluciones es [marca de tiempo 3, ganancia3]. En el sistema de backtesting en línea de FMZ, los datos de la matriz de ganancias se proporcionan a esta función por el sistema de backtesting. Por supuesto, si usted registra los datos de devolución por sí mismo y forma una estructura de matriz, también puede proporcionarlos a la función de cálculo para calcular los resultados.
el La fecha de inicio de la prueba de retroceso.
el La fecha final de la prueba de retroceso.
el período Período de cálculo en milisegundos.
A continuación, vamos a ver la salida de esta función juntos:
return {
totalAssets: totalAssets,
yearDays: yearDays,
totalReturns: totalReturns,
annualizedReturns: annualizedReturns,
sharpeRatio: sharpeRatio,
volatility: volatility,
maxDrawdown: maxDrawdown,
maxDrawdownTime: maxDrawdownTime,
maxAssetsTime: maxAssetsTime,
maxDrawdownStartTime: maxDrawdownStartTime,
winningRate: winningRate
}
Conociendo la entrada y la salida, podemos entender para qué se utiliza la función. Para decirlo simplemente, le da a la función algunos registros originales, como la matriz de estadísticas de retorno. La función le dará un resultado para mostrar el rendimiento de la prueba de retroceso.
A continuación, veamos cómo se calcula el código:
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
}
}
En general, el algoritmo no es complejo, y puede haber varios conceptos que deben entenderse de antemano.
Variación: Se puede entender como un conjunto de datos de retorno. El grupo de muestras, 1, 2, 3, 4 y 5, tiene un valor medio de (1+2+3+4+5)/5 = 3, mientras que la varianza es el valor medio de la suma de los cuadrados de las diferencias entre cada dato y su suma respectivamente, que es [(1-3) ^ 2 + (2-3) ^ 2 + (3-3) ^ 2 + (4-3) ^ 2 + (5-3) ^ 2) / 5 = 2, y la varianza es 2.
Desviación tipo: Calcula la raíz cuadrada aritmética de la varianza, que es la desviación estándar.
La volatilidad: Cuando la escala de cálculo es anualizada, la volatilidad es la desviación típica.
Con la comprensión de estos conceptos y fórmulas de cálculo, la parte de cálculo de Sharpe de la función será clara a simple vista. Fórmula de Sharpe para calcular el coeficiente de Sharpe: (tasa de rendimiento anualizada - tasa libre de riesgo) / desviación típica
¿Ya lo has aprendido?