Einige Performance-Indikator-Algorithmen der Strategie werden häufig von Gruppenmitgliedern diskutiert, und ein Algorithmus wurde auch im API-Dokument von FMZ veröffentlicht. Allerdings ist es ohne Kommentare nicht leicht zu verstehen. In diesem Artikel werde ich Sie zum Analysieren des Algorithmus führen. Ich glaube, dass Sie nach dem Lesen dieses Artikels ein klares Verständnis für die Konzepte von Sharpe-Verhältnis, maximaler Auslastung, Rendite und der Berechnungslogik haben werden. Wir beginnen mit dem Quellcode, der in der JavaScript-Sprache geschrieben ist. Das Backtesting-System von FMZ verwendet diesen Algorithmus auch, um automatisch Backtesting-Leistungsdaten zu generieren.
function returnAnalyze(totalAssets, profits, ts, te, period, yearDays)
https://www.fmz.com/api#BacktestingSystem Sharpe-Algorithmus
Da es sich um eine Berechnungsfunktion handelt, müssen Eingaben und Ausgänge vorhanden sein.
totalAssets, profits, ts, te, period, yearDays
Gesamtvermögen Dieser Parameter ist das anfängliche Gesamtvermögen, wenn die Strategie in Betrieb genommen wird.
Gewinne
Dieser Parameter ist wichtig, da auf der Grundlage dieser ursprünglichen Daten eine Reihe von Leistungsindikatoren berechnet wird.[[timestamp1, profit1], [timestamp2, profit2], [timestamp3, profit3],..., [timestampN, profitN]]
. Es kann gesehen werden, dass die returnAnalyze-Funktion eine solche Datenstruktur benötigt, die die chronologische Reihenfolge der Renditen zu jeder Zeit aufzeichnet. Timestamp1 bis TimestampN sind in chronologischer Reihenfolge von fern nach nahe. An jedem Zeitpunkt gibt es einen Gewinnwert. Zum Beispiel ist der dritte Zeitpunkt im Renditenrekord [Timestamp 3, profit3]. Im Online-Backtesting-System von FMZ werden die Daten des Profits-Arrays dieser Funktion vom Backtesting-System zur Verfügung gestellt. Wenn Sie die Renditendaten selbst aufzeichnen und eine solche Arraystruktur bilden, können Sie sie natürlich auch der Berechnungsfunktion zur Berechnung der Ergebnisse zur Verfügung stellen.
t Der Startzeitstempel des Backtests.
Die Der Zeitstempel für das Ende des Backtests.
Periode Berechnungszeit in Millisekunden.
Als nächstes schauen wir uns gemeinsam die Ausgabe dieser Funktion an:
return {
totalAssets: totalAssets,
yearDays: yearDays,
totalReturns: totalReturns,
annualizedReturns: annualizedReturns,
sharpeRatio: sharpeRatio,
volatility: volatility,
maxDrawdown: maxDrawdown,
maxDrawdownTime: maxDrawdownTime,
maxAssetsTime: maxAssetsTime,
maxDrawdownStartTime: maxDrawdownStartTime,
winningRate: winningRate
}
Wenn wir die Eingabe und Ausgabe kennen, können wir verstehen, wofür die Funktion verwendet wird. Einfach ausgedrückt, gibt sie der Funktion einige ursprüngliche Datensätze, wie z.B. das Return-Statistik-Array. Die Funktion gibt Ihnen ein Ergebnis, um die Leistung des Backtests zu zeigen.
Als nächstes sehen wir, wie der Code berechnet wird:
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
}
}
Insgesamt ist der Algorithmus nicht komplex, und es kann mehrere Konzepte geben, die im Voraus verstanden werden müssen.
Abweichung: Es kann als eine Reihe von Rückgabeangaben verstanden werden. Die Probengruppe 1, 2, 3, 4 und 5 hat einen Mittelwert von (1+2+3+4+5)/5 = 3, während die Varianz der Mittelwert der Summe der Quadrate der Unterschiede zwischen den einzelnen Daten und ihrer Summe ist, was [(1-3) ^ 2 + (2-3) ^ 2 + (3-3) ^ 2 + (4-3) ^ 2 + (5-3) ^ 2) / 5 = 2 ist, und die Varianz ist 2.
Standardabweichung: Berechnen Sie die arithmetische Quadratwurzel der Varianz, die die Standardabweichung ist.
Volatilität: Wenn die Berechnungsskala auf Jahresbasis ermittelt wird, ist die Volatilität die Standardabweichung.
Mit dem Verständnis dieser Konzepte und Berechnungsformeln wird der Sharpe-Rechenteil der Funktion auf einen Blick klar. Sharpe-Formel zur Berechnung der Sharpe-Ratio: (jährliche Rendite - risikofreie Rate) / Standardabweichung
Hast du es schon gelernt?